metadata_helpers.py revision c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61
1da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin#
2da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# Copyright (C) 2012 The Android Open Source Project
3da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin#
4da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# Licensed under the Apache License, Version 2.0 (the "License");
5da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# you may not use this file except in compliance with the License.
6da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# You may obtain a copy of the License at
7da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin#
8da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin#      http://www.apache.org/licenses/LICENSE-2.0
9da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin#
10da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# Unless required by applicable law or agreed to in writing, software
11da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# distributed under the License is distributed on an "AS IS" BASIS,
12da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# See the License for the specific language governing permissions and
14da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# limitations under the License.
15da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin#
16da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
17da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin"""
18da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor MurashkinA set of helpers for rendering Mako templates with a Metadata model.
19da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin"""
20da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
21da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkinimport metadata_model
22aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkinimport re
238aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkinimport markdown
248aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkinimport textwrap
2563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvalaimport sys
260b080452cca90f215d10d636abfb47701d7518daIgor Murashkinimport bs4
270b080452cca90f215d10d636abfb47701d7518daIgor Murashkin# Monkey-patch BS4. WBR element must not have an end tag.
280b080452cca90f215d10d636abfb47701d7518daIgor Murashkinbs4.builder.HTMLTreeBuilder.empty_element_tags.add("wbr")
298aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
30586c861e6dab3fdf48fc8440c719ad0b59d49d72Igor Murashkinfrom collections import OrderedDict
31da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
321dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin# Relative path from HTML file to the base directory used by <img> tags
331dd4ecb0ea0589610b3616459b707c2898889153Igor MurashkinIMAGE_SRC_METADATA="images/camera2/metadata/"
341dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
351dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin# Prepend this path to each <img src="foo"> in javadocs
361dd4ecb0ea0589610b3616459b707c2898889153Igor MurashkinJAVADOC_IMAGE_SRC_METADATA="../../../../" + IMAGE_SRC_METADATA
371dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
38da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin_context_buf = None
39da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
40da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef _is_sec_or_ins(x):
41da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return isinstance(x, metadata_model.Section) or    \
42da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin         isinstance(x, metadata_model.InnerNamespace)
43da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
44da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin##
45da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin## Metadata Helpers
46da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin##
47da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
48da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef find_all_sections(root):
49da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
50da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Find all descendants that are Section or InnerNamespace instances.
51da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
52da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
53da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    root: a Metadata instance
54da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
55da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
56da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A list of Section/InnerNamespace instances
57da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
58da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
59da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    These are known as "sections" in the generated C code.
60da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
61da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return root.find_all(_is_sec_or_ins)
62da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
63da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef find_parent_section(entry):
64da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
65da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Find the closest ancestor that is either a Section or InnerNamespace.
66da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
67da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
68da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    entry: an Entry or Clone node
69da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
70da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
71da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    An instance of Section or InnerNamespace
72da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
73da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return entry.find_parent_first(_is_sec_or_ins)
74da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
75da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# find uniquely named entries (w/o recursing through inner namespaces)
76da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef find_unique_entries(node):
77da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
78da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Find all uniquely named entries, without recursing through inner namespaces.
79da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
80da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
81da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    node: a Section or InnerNamespace instance
82da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
83da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Yields:
84da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A sequence of MergedEntry nodes representing an entry
85da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
86da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
87da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    This collapses multiple entries with the same fully qualified name into
88da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    one entry (e.g. if there are multiple entries in different kinds).
89da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
90da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  if not isinstance(node, metadata_model.Section) and    \
91da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin     not isinstance(node, metadata_model.InnerNamespace):
92da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      raise TypeError("expected node to be a Section or InnerNamespace")
93da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
94586c861e6dab3fdf48fc8440c719ad0b59d49d72Igor Murashkin  d = OrderedDict()
95da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  # remove the 'kinds' from the path between sec and the closest entries
96da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  # then search the immediate children of the search path
97da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  search_path = isinstance(node, metadata_model.Section) and node.kinds \
98da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin                or [node]
99da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  for i in search_path:
100da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      for entry in i.entries:
101da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin          d[entry.name] = entry
102da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
103da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  for k,v in d.iteritems():
104da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      yield v.merge()
105da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
106da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef path_name(node):
107da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
108da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Calculate a period-separated string path from the root to this element,
109da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  by joining the names of each node and excluding the Metadata/Kind nodes
110da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  from the path.
111da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
112da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
113da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    node: a Node instance
114da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
115da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
116da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A string path
117da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
118da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
119da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  isa = lambda x,y: isinstance(x, y)
120da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  fltr = lambda x: not isa(x, metadata_model.Metadata) and \
121da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin                   not isa(x, metadata_model.Kind)
122da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
123da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  path = node.find_parents(fltr)
124da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  path = list(path)
125da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  path.reverse()
126da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  path.append(node)
127da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
128da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return ".".join((i.name for i in path))
129da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
130ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yehdef ndk(name):
131ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  """
132ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  Return the NDK version of given name, which replace
133ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  the leading "android" to "acamera"
134ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh
135ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  Args:
136ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh    name: name string of an entry
137ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh
138ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  Returns:
139ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh    A NDK version name string of the input name
140ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  """
141ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  name_list = name.split(".")
142ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  if name_list[0] == "android":
143ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh    name_list[0] = "acamera"
144ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  return ".".join(name_list)
145ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh
146c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yehdef protobuf_type(entry):
147c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  """
148c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Return the protocol buffer message type for input metadata entry.
149c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Only support types used by static metadata right now
150c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
151c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Returns:
152c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    A string of protocol buffer type. Ex: "optional int32" or "repeated RangeInt"
153c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  """
154c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  typeName = None
155c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  if entry.typedef is None:
156c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    typeName = entry.type
157c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  else:
158c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    typeName = entry.typedef.name
159c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
160c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  typename_to_protobuftype = {
161c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "rational"               : "Rational",
162c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "size"                   : "Size",
163c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "sizeF"                  : "SizeF",
164c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "rectangle"              : "Rect",
165c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "streamConfigurationMap" : "StreamConfigurations",
166c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "rangeInt"               : "RangeInt",
167c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "rangeLong"              : "RangeLong",
168c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "colorSpaceTransform"    : "ColorSpaceTransform",
169c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "blackLevelPattern"      : "BlackLevelPattern",
170c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "byte"                   : "int32", # protocol buffer don't support byte
171c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "boolean"                : "bool",
172c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "float"                  : "float",
173c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "double"                 : "double",
174c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "int32"                  : "int32",
175c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "int64"                  : "int64",
176c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "enumList"               : "int32"
177c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  }
178c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
179c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  if typeName not in typename_to_protobuftype:
180c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    print >> sys.stderr,\
181c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh      "  ERROR: Could not find protocol buffer type for {%s} type {%s} typedef {%s}" % \
182c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh          (entry.name, entry.type, entry.typedef)
183c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
184c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  proto_type = typename_to_protobuftype[typeName]
185c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
186c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  prefix = "optional"
187c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  if entry.container == 'array':
188c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    has_variable_size = False
189c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    for size in entry.container_sizes:
190c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh      try:
191c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh        size_int = int(size)
192c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh      except ValueError:
193c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh        has_variable_size = True
194c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
195c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    if has_variable_size:
196c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh      prefix = "repeated"
197c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
198c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  return "%s %s" %(prefix, proto_type)
199c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
200c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
201c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yehdef protobuf_name(entry):
202c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  """
203c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Return the protocol buffer field name for input metadata entry
204c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
205c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Returns:
206c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    A string. Ex: "android_colorCorrection_availableAberrationModes"
207c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  """
208c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  return entry.name.replace(".", "_")
209c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
210aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef has_descendants_with_enums(node):
211aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
212aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Determine whether or not the current node is or has any descendants with an
213aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Enum node.
214aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
215aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
216aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    node: a Node instance
217aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
218aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
219aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    True if it finds an Enum node in the subtree, False otherwise
220aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
221aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  return bool(node.find_first(lambda x: isinstance(x, metadata_model.Enum)))
222aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
223aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef get_children_by_throwing_away_kind(node, member='entries'):
224aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
225aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Get the children of this node by compressing the subtree together by removing
226aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  the kind and then combining any children nodes with the same name together.
227aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
228aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
229aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    node: An instance of Section, InnerNamespace, or Kind
230aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
231aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
232aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    An iterable over the combined children of the subtree of node,
233aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    as if the Kinds never existed.
234aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
235aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Remarks:
236aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    Not recursive. Call this function repeatedly on each child.
237aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
238aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
239aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if isinstance(node, metadata_model.Section):
240aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    # Note that this makes jump from Section to Kind,
241aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    # skipping the Kind entirely in the tree.
242aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    node_to_combine = node.combine_kinds_into_single_node()
243aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  else:
244aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    node_to_combine = node
245aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
246aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  combined_kind = node_to_combine.combine_children_by_name()
247aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
248aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  return (i for i in getattr(combined_kind, member))
249aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
250aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef get_children_by_filtering_kind(section, kind_name, member='entries'):
251aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
25223d4b2c087bd4286bf16bda83b6d9f72c5bb1718Eino-Ville Talvala  Takes a section and yields the children of the merged kind under this section.
253aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
254aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
255aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    section: An instance of Section
256aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    kind_name: A name of the kind, i.e. 'dynamic' or 'static' or 'controls'
257aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
258aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
25923d4b2c087bd4286bf16bda83b6d9f72c5bb1718Eino-Ville Talvala    An iterable over the children of the specified merged kind.
260aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
261aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
26223d4b2c087bd4286bf16bda83b6d9f72c5bb1718Eino-Ville Talvala  matched_kind = next((i for i in section.merged_kinds if i.name == kind_name), None)
263aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
264aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if matched_kind:
265aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return getattr(matched_kind, member)
266aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  else:
267aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return ()
268aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
269da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin##
270da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin## Filters
271da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin##
272da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
273da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# abcDef.xyz -> ABC_DEF_XYZ
274da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef csym(name):
275da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
276da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Convert an entry name string into an uppercase C symbol.
277da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
278da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
279da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A string
280da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
281da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
282da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    csym('abcDef.xyz') == 'ABC_DEF_XYZ'
283da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
284da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  newstr = name
285da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  newstr = "".join([i.isupper() and ("_" + i) or i for i in newstr]).upper()
286da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  newstr = newstr.replace(".", "_")
287da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return newstr
288da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
289da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# abcDef.xyz -> abc_def_xyz
290da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef csyml(name):
291da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
292da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Convert an entry name string into a lowercase C symbol.
293da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
294da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
295da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A string
296da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
297da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
298da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    csyml('abcDef.xyz') == 'abc_def_xyz'
299da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
300da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return csym(name).lower()
301da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
302da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# pad with spaces to make string len == size. add new line if too big
303da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef ljust(size, indent=4):
304da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
305da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Creates a function that given a string will pad it with spaces to make
306da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  the string length == size. Adds a new line if the string was too big.
307da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
308da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
309da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    size: an integer representing how much spacing should be added
310da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    indent: an integer representing the initial indendation level
311da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
312da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
313da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A function that takes a string and returns a string.
314da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
315da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
316da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    ljust(8)("hello") == 'hello   '
317da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
318da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
319da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    Deprecated. Use pad instead since it works for non-first items in a
320da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    Mako template.
321da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
322da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  def inner(what):
323da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    newstr = what.ljust(size)
324da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    if len(newstr) > size:
325da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      return what + "\n" + "".ljust(indent + size)
326da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    else:
327da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      return newstr
328da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return inner
329da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
330da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef _find_new_line():
331da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
332da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  if _context_buf is None:
333da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    raise ValueError("Context buffer was not set")
334da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
335da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  buf = _context_buf
336da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  x = -1 # since the first read is always ''
337da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  cur_pos = buf.tell()
338da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  while buf.tell() > 0 and buf.read(1) != '\n':
339da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    buf.seek(cur_pos - x)
340da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    x = x + 1
341da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
342da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  buf.seek(cur_pos)
343da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
344da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return int(x)
345da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
346da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# Pad the string until the buffer reaches the desired column.
347da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# If string is too long, insert a new line with 'col' spaces instead
348da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef pad(col):
349da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
350da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Create a function that given a string will pad it to the specified column col.
351da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  If the string overflows the column, put the string on a new line and pad it.
352da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
353da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
354da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    col: an integer specifying the column number
355da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
356da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
357da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A function that given a string will produce a padded string.
358da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
359da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
360da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    pad(8)("hello") == 'hello   '
361da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
362da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
363da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    This keeps track of the line written by Mako so far, so it will always
364da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    align to the column number correctly.
365da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
366da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  def inner(what):
367da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    wut = int(col)
368da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    current_col = _find_new_line()
369da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
370da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    if len(what) > wut - current_col:
371da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      return what + "\n".ljust(col)
372da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    else:
373da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      return what.ljust(wut - current_col)
374da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return inner
375da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
376da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# int32 -> TYPE_INT32, byte -> TYPE_BYTE, etc. note that enum -> TYPE_INT32
377da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef ctype_enum(what):
378da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
379da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Generate a camera_metadata_type_t symbol from a type string.
380da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
381da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
382da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    what: a type string
383da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
384da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
385da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A string representing the camera_metadata_type_t
386da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
387da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
388da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    ctype_enum('int32') == 'TYPE_INT32'
389da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    ctype_enum('int64') == 'TYPE_INT64'
390da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    ctype_enum('float') == 'TYPE_FLOAT'
391da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
392da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
393e6b664671e35984156e06e17531311a09864ac8bIgor Murashkin    An enum is coerced to a byte since the rest of the camera_metadata
394da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    code doesn't support enums directly yet.
395da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
396da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return 'TYPE_%s' %(what.upper())
397aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
398b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
399b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin# Calculate a java type name from an entry with a Typedef node
400b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef _jtypedef_type(entry):
401b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  typedef = entry.typedef
402b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  additional = ''
403b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
404b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # Hacky way to deal with arrays. Assume that if we have
405b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # size 'Constant x N' the Constant is part of the Typedef size.
406b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # So something sized just 'Constant', 'Constant1 x Constant2', etc
407b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # is not treated as a real java array.
408b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  if entry.container == 'array':
409b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    has_variable_size = False
410b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    for size in entry.container_sizes:
411b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      try:
412b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        size_int = int(size)
413b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      except ValueError:
414b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        has_variable_size = True
415b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
416b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    if has_variable_size:
417b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      additional = '[]'
418b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
419b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  try:
420b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    name = typedef.languages['java']
421b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
422b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    return "%s%s" %(name, additional)
423b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  except KeyError:
424b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    return None
425b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
426b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin# Box if primitive. Otherwise leave unboxed.
427b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef _jtype_box(type_name):
428b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  mapping = {
429b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'boolean': 'Boolean',
430b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'byte': 'Byte',
431b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'int': 'Integer',
432b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'float': 'Float',
433b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'double': 'Double',
434b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'long': 'Long'
435b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  }
436b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
437b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  return mapping.get(type_name, type_name)
438b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
439b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef jtype_unboxed(entry):
440aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
441b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  Calculate the Java type from an entry type string, to be used whenever we
442b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  need the regular type in Java. It's not boxed, so it can't be used as a
443b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  generic type argument when the entry type happens to resolve to a primitive.
444aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
445aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Remarks:
446aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    Since Java generics cannot be instantiated with primitives, this version
447b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    is not applicable in that case. Use jtype_boxed instead for that.
448aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
449aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
450aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    The string representing the Java type.
451aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
452aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if not isinstance(entry, metadata_model.Entry):
453aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    raise ValueError("Expected entry to be an instance of Entry")
454aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
455b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  metadata_type = entry.type
456aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
457b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  java_type = None
458aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
459b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  if entry.typedef:
460b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    typedef_name = _jtypedef_type(entry)
461b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    if typedef_name:
462b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      java_type = typedef_name # already takes into account arrays
463aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
464b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  if not java_type:
465e04dbd44c7d2043c31107ff3f9efdad71f438071Zhijun He    if not java_type and entry.enum and metadata_type == 'byte':
466e04dbd44c7d2043c31107ff3f9efdad71f438071Zhijun He      # Always map byte enums to Java ints, unless there's a typedef override
467d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala      base_type = 'int'
468aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
469b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    else:
470b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      mapping = {
471b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'int32': 'int',
472b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'int64': 'long',
473b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'float': 'float',
474b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'double': 'double',
475b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'byte': 'byte',
476b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'rational': 'Rational'
477b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      }
478b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
479b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      base_type = mapping[metadata_type]
480b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
481b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    # Convert to array (enums, basic types)
482b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    if entry.container == 'array':
483b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      additional = '[]'
484b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    else:
485b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      additional = ''
486b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
487b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    java_type = '%s%s' %(base_type, additional)
488b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
489b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # Now box this sucker.
490b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  return java_type
491b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
492b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef jtype_boxed(entry):
493b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  """
494b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  Calculate the Java type from an entry type string, to be used as a generic
495b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  type argument in Java. The type is guaranteed to inherit from Object.
496b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
497b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  It will only box when absolutely necessary, i.e. int -> Integer[], but
498b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  int[] -> int[].
499aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
500b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  Remarks:
501b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    Since Java generics cannot be instantiated with primitives, this version
502b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    will use boxed types when absolutely required.
503aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
504b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  Returns:
505b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    The string representing the boxed Java type.
506b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  """
507b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  unboxed_type = jtype_unboxed(entry)
508b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  return _jtype_box(unboxed_type)
509b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
51035a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkindef _is_jtype_generic(entry):
51135a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  """
51235a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Determine whether or not the Java type represented by the entry type
51335a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  string and/or typedef is a Java generic.
51435a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
51535a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  For example, "Range<Integer>" would be considered a generic, whereas
51635a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  a "MeteringRectangle" or a plain "Integer" would not be considered a generic.
51735a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
51835a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Args:
51935a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    entry: An instance of an Entry node
52035a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
52135a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Returns:
52235a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    True if it's a java generic, False otherwise.
52335a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  """
52435a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  if entry.typedef:
52535a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    local_typedef = _jtypedef_type(entry)
52635a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    if local_typedef:
52735a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin      match = re.search(r'<.*>', local_typedef)
52835a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin      return bool(match)
52935a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  return False
53035a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
531b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef _jtype_primitive(what):
532aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
533aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Calculate the Java type from an entry type string.
534aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
535aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Remarks:
536aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    Makes a special exception for Rational, since it's a primitive in terms of
537aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    the C-library camera_metadata type system.
538aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
539aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
540aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    The string representing the primitive type
541aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
542aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  mapping = {
543aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'int32': 'int',
544aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'int64': 'long',
545aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'float': 'float',
546aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'double': 'double',
547aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'byte': 'byte',
548aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'rational': 'Rational'
549aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  }
550aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
551aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  try:
552aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return mapping[what]
553aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  except KeyError as e:
554aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    raise ValueError("Can't map '%s' to a primitive, not supported" %what)
555aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
556aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef jclass(entry):
557aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
558aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Calculate the java Class reference string for an entry.
559aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
560aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
561aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    entry: an Entry node
562aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
563aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Example:
564aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    <entry name="some_int" type="int32"/>
565aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    <entry name="some_int_array" type="int32" container='array'/>
566aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
567aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    jclass(some_int) == 'int.class'
568aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    jclass(some_int_array) == 'int[].class'
569aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
570aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
571aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    The ClassName.class string
572aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
573aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
574b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  return "%s.class" %jtype_unboxed(entry)
575aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
57635a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkindef jkey_type_token(entry):
57735a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  """
57835a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Calculate the java type token compatible with a Key constructor.
57935a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  This will be the Java Class<T> for non-generic classes, and a
58035a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  TypeReference<T> for generic classes.
58135a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
58235a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Args:
58335a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    entry: An entry node
58435a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
58535a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Returns:
58635a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    The ClassName.class string, or 'new TypeReference<ClassName>() {{ }}' string
58735a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  """
58835a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  if _is_jtype_generic(entry):
58935a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    return "new TypeReference<%s>() {{ }}" %(jtype_boxed(entry))
59035a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  else:
59135a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    return jclass(entry)
59235a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
593aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef jidentifier(what):
594aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
595aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Convert the input string into a valid Java identifier.
596aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
597aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
598aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    what: any identifier string
599aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
600aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
601aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    String with added underscores if necessary.
602aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
603aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if re.match("\d", what):
604aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return "_%s" %what
605aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  else:
606aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return what
607aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
608aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef enum_calculate_value_string(enum_value):
609aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
610aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Calculate the value of the enum, even if it does not have one explicitly
611aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  defined.
612aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
613aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  This looks back for the first enum value that has a predefined value and then
614aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  applies addition until we get the right value, using C-enum semantics.
615aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
616aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
617aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    enum_value: an EnumValue node with a valid Enum parent
618aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
619aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Example:
620aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    <enum>
621aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      <value>X</value>
622aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      <value id="5">Y</value>
623aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      <value>Z</value>
624aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    </enum>
625aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
626aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    enum_calculate_value_string(X) == '0'
627aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    enum_calculate_Value_string(Y) == '5'
628aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    enum_calculate_value_string(Z) == '6'
629aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
630aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
631aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    String that represents the enum value as an integer literal.
632aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
633aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
634aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  enum_value_siblings = list(enum_value.parent.values)
635aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  this_index = enum_value_siblings.index(enum_value)
636aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
637aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  def is_hex_string(instr):
638aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return bool(re.match('0x[a-f0-9]+$', instr, re.IGNORECASE))
639aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
640aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  base_value = 0
641aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  base_offset = 0
642aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  emit_as_hex = False
643aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
644aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  this_id = enum_value_siblings[this_index].id
645aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  while this_index != 0 and not this_id:
646aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    this_index -= 1
647aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    base_offset += 1
648aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    this_id = enum_value_siblings[this_index].id
649aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
650aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if this_id:
651aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    base_value = int(this_id, 0)  # guess base
652aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    emit_as_hex = is_hex_string(this_id)
653aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
654aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if emit_as_hex:
655aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return "0x%X" %(base_value + base_offset)
656aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  else:
657aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return "%d" %(base_value + base_offset)
658aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
659aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef enumerate_with_last(iterable):
660aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
661aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Enumerate a sequence of iterable, while knowing if this element is the last in
662aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  the sequence or not.
663aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
664aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
665aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    iterable: an Iterable of some sequence
666aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
667aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Yields:
668aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    (element, bool) where the bool is True iff the element is last in the seq.
669aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
670aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  it = (i for i in iterable)
671aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
672aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  first = next(it)  # OK: raises exception if it is empty
673aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
674aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  second = first  # for when we have only 1 element in iterable
675aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
676aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  try:
677aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    while True:
678aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      second = next(it)
679aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      # more elements remaining.
680aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      yield (first, False)
681aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      first = second
682aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  except StopIteration:
683aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    # last element. no more elements left
684aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    yield (second, True)
685aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
686aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef pascal_case(what):
687aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
688aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Convert the first letter of a string to uppercase, to make the identifier
689aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  conform to PascalCase.
690aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
6915250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  If there are dots, remove the dots, and capitalize the letter following
6925250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  where the dot was. Letters that weren't following dots are left unchanged,
6935250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  except for the first letter of the string (which is made upper-case).
6945250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
695aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
696aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    what: a string representing some identifier
697aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
698aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
699aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    String with first letter capitalized
700aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
701aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Example:
702aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    pascal_case("helloWorld") == "HelloWorld"
703aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    pascal_case("foo") == "Foo"
7045250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    pascal_case("hello.world") = "HelloWorld"
7055250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    pascal_case("fooBar.fooBar") = "FooBarFooBar"
7065250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  """
7075250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  return "".join([s[0:1].upper() + s[1:] for s in what.split('.')])
7085250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
7095250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knightdef jkey_identifier(what):
7105250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  """
7115250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  Return a Java identifier from a property name.
7125250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
7135250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  Args:
7145250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    what: a string representing a property name.
7155250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
7165250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  Returns:
7175250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    Java identifier corresponding to the property name. May need to be
7185250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    prepended with the appropriate Java class name by the caller of this
7195250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    function. Note that the outer namespace is stripped from the property
7205250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    name.
7215250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
7225250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  Example:
723d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    jkey_identifier("android.lens.facing") == "LENS_FACING"
724aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
725d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  return csym(what[what.find('.') + 1:])
726aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
727d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvaladef jenum_value(enum_entry, enum_value):
728aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
729d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  Calculate the Java name for an integer enum value
730aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
731aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
732d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    enum: An enum-typed Entry node
733d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    value: An EnumValue node for the enum
734aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
735aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
736aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    String representing the Java symbol
737aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
738aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
739d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  cname = csym(enum_entry.name)
740d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  return cname[cname.find('_') + 1:] + '_' + enum_value.name
741d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
742567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvaladef generate_extra_javadoc_detail(entry):
743567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  """
744567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  Returns a function to add extra details for an entry into a string for inclusion into
745567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  javadoc. Adds information about units, the list of enum values for this key, and the valid
746567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  range.
747567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  """
748567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  def inner(text):
749567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.units:
750567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '\n\n<b>Units</b>: %s\n' % (dedent(entry.units))
751567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.enum and not (entry.typedef and entry.typedef.languages.get('java')):
752567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '\n\n<b>Possible values:</b>\n<ul>\n'
753567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      for value in entry.enum.values:
754567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        if not value.hidden:
755567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala          text += '  <li>{@link #%s %s}</li>\n' % ( jenum_value(entry, value ), value.name )
756567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '</ul>\n'
757567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.range:
758567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      if entry.enum and not (entry.typedef and entry.typedef.languages.get('java')):
759567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        text += '\n\n<b>Available values for this device:</b><br>\n'
760567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      else:
761567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        text += '\n\n<b>Range of valid values:</b><br>\n'
762567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '%s\n' % (dedent(entry.range))
763567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.hwlevel != 'legacy': # covers any of (None, 'limited', 'full')
764567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '\n\n<b>Optional</b> - This value may be {@code null} on some devices.\n'
765567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.hwlevel == 'full':
766567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += \
767567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        '\n<b>Full capability</b> - \n' + \
768567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        'Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the\n' + \
769567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        'android.info.supportedHardwareLevel key\n'
770567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.hwlevel == 'limited':
771567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += \
772567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        '\n<b>Limited capability</b> - \n' + \
773567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        'Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the\n' + \
774567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        'android.info.supportedHardwareLevel key\n'
775567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.hwlevel == 'legacy':
776567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += "\nThis key is available on all devices."
777567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala
778567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    return text
779567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  return inner
780567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala
781567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala
78263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvaladef javadoc(metadata, indent = 4):
783d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  """
78463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala  Returns a function to format a markdown syntax text block as a
78563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala  javadoc comment section, given a set of metadata
786d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
787d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  Args:
78863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    metadata: A Metadata instance, representing the the top-level root
78963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      of the metadata for cross-referencing
790d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    indent: baseline level of indentation for javadoc block
791d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  Returns:
79263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    A function that transforms a String text block as follows:
793d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    - Indent and * for insertion into a Javadoc comment block
7948aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    - Trailing whitespace removed
7958aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    - Entire body rendered via markdown to generate HTML
79663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    - All tag names converted to appropriate Javadoc {@link} with @see
79763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      for each tag
798d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
799d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  Example:
800d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "This is a comment for Javadoc\n" +
801d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "     with multiple lines, that should be   \n" +
802d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "     formatted better\n" +
803d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "\n" +
804d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "    That covers multiple lines as well\n"
80563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    "    And references android.control.mode\n"
806d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
807d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    transforms to
8088aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    "    * <p>This is a comment for Javadoc\n" +
809d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "    * with multiple lines, that should be\n" +
8108aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    "    * formatted better</p>\n" +
8118aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    "    * <p>That covers multiple lines as well</p>\n" +
81263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    "    * and references {@link CaptureRequest#CONTROL_MODE android.control.mode}\n" +
81363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    "    *\n" +
81463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    "    * @see CaptureRequest#CONTROL_MODE\n"
815d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  """
81663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala  def javadoc_formatter(text):
81763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    comment_prefix = " " * indent + " * ";
818d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
81963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # render with markdown => HTML
82063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    javatext = md(text, JAVADOC_IMAGE_SRC_METADATA)
821d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
822ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    # Identity transform for javadoc links
823ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    def javadoc_link_filter(target, shortname):
824ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return '{@link %s %s}' % (target, shortname)
825ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
826ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    javatext = filter_links(javatext, javadoc_link_filter)
827ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
82863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # Crossref tag names
82963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    kind_mapping = {
83063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        'static': 'CameraCharacteristics',
83163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        'dynamic': 'CaptureResult',
83263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        'controls': 'CaptureRequest' }
833aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
83463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # Convert metadata entry "android.x.y.z" to form
83563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # "{@link CaptureRequest#X_Y_Z android.x.y.z}"
83663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    def javadoc_crossref_filter(node):
837c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh      if node.applied_visibility in ('public', 'java_public'):
83863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        return '{@link %s#%s %s}' % (kind_mapping[node.kind],
83963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala                                     jkey_identifier(node.name),
84063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala                                     node.name)
84163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      else:
84263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        return node.name
843aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
84463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # For each public tag "android.x.y.z" referenced, add a
84563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # "@see CaptureRequest#X_Y_Z"
846ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    def javadoc_crossref_see_filter(node_set):
847c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh      node_set = (x for x in node_set if x.applied_visibility in ('public', 'java_public'))
84863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
84963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      text = '\n'
85063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      for node in node_set:
85163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        text = text + '\n@see %s#%s' % (kind_mapping[node.kind],
85263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala                                      jkey_identifier(node.name))
85363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
85463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      return text if text != '\n' else ''
85563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
856ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    javatext = filter_tags(javatext, metadata, javadoc_crossref_filter, javadoc_crossref_see_filter)
85763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
85863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    def line_filter(line):
85963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      # Indent each line
86063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      # Add ' * ' to it for stylistic reasons
86163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      # Strip right side of trailing whitespace
862567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      return (comment_prefix + line).rstrip()
86363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
86463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # Process each line with above filter
86563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    javatext = "\n".join(line_filter(i) for i in javatext.split("\n")) + "\n"
86663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
86763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    return javatext
86863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
86963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala  return javadoc_formatter
870aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
87188b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkindef dedent(text):
87288b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  """
87388b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  Remove all common indentation from every line but the 0th.
87488b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  This will avoid getting <code> blocks when rendering text via markdown.
87588b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  Ignoring the 0th line will also allow the 0th line not to be aligned.
87688b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
87788b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  Args:
87888b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    text: A string of text to dedent.
87988b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
88088b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  Returns:
88188b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    String dedented by above rules.
88288b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
88388b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  For example:
88488b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    assertEquals("bar\nline1\nline2",   dedent("bar\n  line1\n  line2"))
88588b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    assertEquals("bar\nline1\nline2",   dedent(" bar\n  line1\n  line2"))
88688b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    assertEquals("bar\n  line1\nline2", dedent(" bar\n    line1\n  line2"))
88788b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  """
88888b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text = textwrap.dedent(text)
88988b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text_lines = text.split('\n')
89088b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text_not_first = "\n".join(text_lines[1:])
89188b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text_not_first = textwrap.dedent(text_not_first)
89288b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text = text_lines[0] + "\n" + text_not_first
89388b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
89488b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  return text
89588b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
8961dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkindef md(text, img_src_prefix=""):
8978aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    """
8988aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    Run text through markdown to produce HTML.
8998aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9008aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    This also removes all common indentation from every line but the 0th.
9018aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    This will avoid getting <code> blocks in markdown.
9028aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    Ignoring the 0th line will also allow the 0th line not to be aligned.
9038aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9041dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    Args:
9051dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin      text: A markdown-syntax using block of text to format.
9061dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin      img_src_prefix: An optional string to prepend to each <img src="target"/>
9071dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
9081dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    Returns:
9091dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin      String rendered by markdown and other rules applied (see above).
9101dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
9118aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    For example, this avoids the following situation:
9128aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9138aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!-- Input -->
9148aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9158aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!--- can't use dedent directly since 'foo' has no indent -->
9168aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <notes>foo
9178aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin          bar
9188aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin          bar
9198aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      </notes>
9208aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9218aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!-- Bad Output -- >
9228aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!-- if no dedent is done generated code looks like -->
9238aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <p>foo
9248aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin        <code><pre>
9258aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin          bar
9268aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin          bar</pre></code>
9278aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      </p>
9288aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9298aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    Instead we get the more natural expected result:
9308aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9318aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!-- Good Output -->
9328aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <p>foo
9338aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      bar
9348aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      bar</p>
9358aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9368aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    """
93788b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    text = dedent(text)
9381dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
939a48441daa766098190b40d5187ce1963d8a980afIgor Murashkin    # full list of extensions at http://pythonhosted.org/Markdown/extensions/
940a48441daa766098190b40d5187ce1963d8a980afIgor Murashkin    md_extensions = ['tables'] # make <table> with ASCII |_| tables
9418aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    # render with markdown
942a48441daa766098190b40d5187ce1963d8a980afIgor Murashkin    text = markdown.markdown(text, md_extensions)
9431dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
9441dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    # prepend a prefix to each <img src="foo"> -> <img src="${prefix}foo">
9451dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    text = re.sub(r'src="([^"]*)"', 'src="' + img_src_prefix + r'\1"', text)
9461dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    return text
9478aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
94863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvaladef filter_tags(text, metadata, filter_function, summary_function = None):
94963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    """
95063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    Find all references to tags in the form outer_namespace.xxx.yyy[.zzz] in
95163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    the provided text, and pass them through filter_function and summary_function.
95263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
95363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    Used to linkify entry names in HMTL, javadoc output.
95463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
95563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    Args:
95663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      text: A string representing a block of text destined for output
95763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      metadata: A Metadata instance, the root of the metadata properties tree
95863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      filter_function: A Node->string function to apply to each node
95963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        when found in text; the string returned replaces the tag name in text.
96050f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala      summary_function: A Node list->string function that is provided the list of
96150f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala        unique tag nodes found in text, and which must return a string that is
96250f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala        then appended to the end of the text. The list is sorted alphabetically
96350f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala        by node name.
96463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    """
96563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
96663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    tag_set = set()
96763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    def name_match(name):
96863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      return lambda node: node.name == name
96963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
97063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # Match outer_namespace.x.y or outer_namespace.x.y.z, making sure
97163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # to grab .z and not just outer_namespace.x.y.  (sloppy, but since we
972ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    # check for validity, a few false positives don't hurt).
973ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    # Try to ignore items of the form {@link <outer_namespace>...
97463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    for outer_namespace in metadata.outer_namespaces:
97563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
976ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      tag_match = r"(?<!\{@link\s)" + outer_namespace.name + \
9777fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        r"\.([a-zA-Z0-9\n]+)\.([a-zA-Z0-9\n]+)(\.[a-zA-Z0-9\n]+)?([/]?)"
97863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
97963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      def filter_sub(match):
98063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        whole_match = match.group(0)
98163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        section1 = match.group(1)
98263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        section2 = match.group(2)
98363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        section3 = match.group(3)
9847fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        end_slash = match.group(4)
9857fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
9867fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        # Don't linkify things ending in slash (urls, for example)
9877fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        if end_slash:
9887fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          return whole_match
9897fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
9907fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        candidate = ""
99163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
99263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        # First try a two-level match
9937fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        candidate2 = "%s.%s.%s" % (outer_namespace.name, section1, section2)
99463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        got_two_level = False
99563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
9967fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        node = metadata.find_first(name_match(candidate2.replace('\n','')))
9977fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        if not node and '\n' in section2:
9987fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          # Linefeeds are ambiguous - was the intent to add a space,
9997fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          # or continue a lengthy name? Try the former now.
10007fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          candidate2b = "%s.%s.%s" % (outer_namespace.name, section1, section2[:section2.find('\n')])
10017fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          node = metadata.find_first(name_match(candidate2b))
10027fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          if node:
10037fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            candidate2 = candidate2b
100463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
100563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        if node:
10067fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          # Have two-level match
100763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          got_two_level = True
10087fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          candidate = candidate2
10097fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        elif section3:
10107fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          # Try three-level match
10117fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          candidate3 = "%s%s" % (candidate2, section3)
10127fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          node = metadata.find_first(name_match(candidate3.replace('\n','')))
10137fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
10147fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          if not node and '\n' in section3:
10157fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            # Linefeeds are ambiguous - was the intent to add a space,
10167fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            # or continue a lengthy name? Try the former now.
10177fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            candidate3b = "%s%s" % (candidate2, section3[:section3.find('\n')])
10187fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            node = metadata.find_first(name_match(candidate3b))
10197fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            if node:
10207fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala              candidate3 = candidate3b
10217fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
10227fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          if node:
10237fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            # Have 3-level match
10247fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            candidate = candidate3
10257fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
10267fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        # Replace match with crossref or complain if a likely match couldn't be matched
102763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
102863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        if node:
102963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          tag_set.add(node)
103063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          return whole_match.replace(candidate,filter_function(node))
103163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        else:
103263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          print >> sys.stderr,\
103363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala            "  WARNING: Could not crossref likely reference {%s}" % (match.group(0))
103463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          return whole_match
103563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
103663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      text = re.sub(tag_match, filter_sub, text)
103763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
103863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    if summary_function is not None:
103950f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala      return text + summary_function(sorted(tag_set, key=lambda x: x.name))
104063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    else:
104163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      return text
104263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
1043ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvaladef filter_links(text, filter_function, summary_function = None):
1044ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    """
1045ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    Find all references to tags in the form {@link xxx#yyy [zzz]} in the
1046ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    provided text, and pass them through filter_function and
1047ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    summary_function.
1048ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1049ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    Used to linkify documentation cross-references in HMTL, javadoc output.
1050ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1051ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    Args:
1052ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      text: A string representing a block of text destined for output
1053ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      metadata: A Metadata instance, the root of the metadata properties tree
1054ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      filter_function: A (string, string)->string function to apply to each 'xxx#yyy',
1055ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala        zzz pair when found in text; the string returned replaces the tag name in text.
1056ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      summary_function: A string list->string function that is provided the list of
1057ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala        unique targets found in text, and which must return a string that is
1058ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala        then appended to the end of the text. The list is sorted alphabetically
1059ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala        by node name.
1060ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1061ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    """
1062ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1063ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    target_set = set()
1064ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    def name_match(name):
1065ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return lambda node: node.name == name
1066ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1067ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    tag_match = r"\{@link\s+([^\s\}]+)([^\}]*)\}"
1068ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1069ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    def filter_sub(match):
1070ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      whole_match = match.group(0)
1071ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      target = match.group(1)
1072ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      shortname = match.group(2).strip()
1073ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1074ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      #print "Found link '%s' as '%s' -> '%s'" % (target, shortname, filter_function(target, shortname))
1075ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1076ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      # Replace match with crossref
1077ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      target_set.add(target)
1078ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return filter_function(target, shortname)
1079ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1080ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    text = re.sub(tag_match, filter_sub, text)
1081ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1082ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    if summary_function is not None:
1083ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return text + summary_function(sorted(target_set))
1084ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    else:
1085ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return text
1086ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1087f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvaladef any_visible(section, kind_name, visibilities):
1088f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  """
1089f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Determine if entries in this section have an applied visibility that's in
1090f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  the list of given visibilities.
1091f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1092f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Args:
1093f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    section: A section of metadata
1094f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    kind_name: A name of the kind, i.e. 'dynamic' or 'static' or 'controls'
1095f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    visibilities: An iterable of visibilities to match against
1096f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1097f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Returns:
1098f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    True if the section has any entries with any of the given visibilities. False otherwise.
1099f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  """
1100f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1101f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  for inner_namespace in get_children_by_filtering_kind(section, kind_name,
1102f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala                                                        'namespaces'):
1103f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    if any(filter_visibility(inner_namespace.merged_entries, visibilities)):
1104f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala      return True
1105f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1106f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  return any(filter_visibility(get_children_by_filtering_kind(section, kind_name,
1107f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala                                                              'merged_entries'),
1108f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala                               visibilities))
1109f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1110f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1111f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvaladef filter_visibility(entries, visibilities):
1112f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  """
1113f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Remove entries whose applied visibility is not in the supplied visibilities.
1114f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1115f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Args:
1116f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    entries: An iterable of Entry nodes
1117f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    visibilities: An iterable of visibilities to filter against
1118f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1119f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Yields:
1120f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    An iterable of Entry nodes
1121f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  """
1122f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  return (e for e in entries if e.applied_visibility in visibilities)
11230b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11246c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkindef remove_synthetic(entries):
11256c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  """
11266c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  Filter the given entries by removing those that are synthetic.
11276c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin
11286c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  Args:
11296c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin    entries: An iterable of Entry nodes
11306c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin
11316c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  Yields:
11326c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin    An iterable of Entry nodes
11336c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  """
11346c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  return (e for e in entries if not e.synthetic)
11356c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin
1136c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yehdef filter_ndk_visible(entries):
1137c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  """
1138c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  Filter the given entries by removing those that are not NDK visible.
1139c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh
1140c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  Args:
1141c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh    entries: An iterable of Entry nodes
1142c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh
1143c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  Yields:
1144c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh    An iterable of Entry nodes
1145c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  """
1146c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  return (e for e in entries if e.applied_ndk_visible == 'true')
1147c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh
11480b080452cca90f215d10d636abfb47701d7518daIgor Murashkindef wbr(text):
11490b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  """
11500b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  Insert word break hints for the browser in the form of <wbr> HTML tags.
11510b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11520b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  Word breaks are inserted inside an HTML node only, so the nodes themselves
11530b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  will not be changed. Attributes are also left unchanged.
11540b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11550b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  The following rules apply to insert word breaks:
11560b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  - For characters in [ '.', '/', '_' ]
11570b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  - For uppercase letters inside a multi-word X.Y.Z (at least 3 parts)
11580b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11590b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  Args:
11600b080452cca90f215d10d636abfb47701d7518daIgor Murashkin    text: A string of text containing HTML content.
11610b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11620b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  Returns:
11630b080452cca90f215d10d636abfb47701d7518daIgor Murashkin    A string with <wbr> inserted by the above rules.
11640b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  """
11650b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  SPLIT_CHARS_LIST = ['.', '_', '/']
11660b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  SPLIT_CHARS = r'([.|/|_/,]+)' # split by these characters
11670b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  CAP_LETTER_MIN = 3 # at least 3 components split by above chars, i.e. x.y.z
11680b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  def wbr_filter(text):
11690b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      new_txt = text
11700b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11710b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # for johnyOrange.appleCider.redGuardian also insert wbr before the caps
11720b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # => johny<wbr>Orange.apple<wbr>Cider.red<wbr>Guardian
11730b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      for words in text.split(" "):
11740b080452cca90f215d10d636abfb47701d7518daIgor Murashkin        for char in SPLIT_CHARS_LIST:
11750b080452cca90f215d10d636abfb47701d7518daIgor Murashkin          # match at least x.y.z, don't match x or x.y
11760b080452cca90f215d10d636abfb47701d7518daIgor Murashkin          if len(words.split(char)) >= CAP_LETTER_MIN:
11770b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            new_word = re.sub(r"([a-z])([A-Z])", r"\1<wbr>\2", words)
11780b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            new_txt = new_txt.replace(words, new_word)
11790b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11800b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # e.g. X/Y/Z -> X/<wbr>Y/<wbr>/Z. also for X.Y.Z, X_Y_Z.
11810b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      new_txt = re.sub(SPLIT_CHARS, r"\1<wbr>", new_txt)
11820b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11830b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      return new_txt
11840b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11850b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  # Do not mangle HTML when doing the replace by using BeatifulSoup
11860b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  # - Use the 'html.parser' to avoid inserting <html><body> when decoding
11870b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  soup = bs4.BeautifulSoup(text, features='html.parser')
11880b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  wbr_tag = lambda: soup.new_tag('wbr') # must generate new tag every time
11890b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11900b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  for navigable_string in soup.findAll(text=True):
11910b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      parent = navigable_string.parent
11920b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11930b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # Insert each '$text<wbr>$foo' before the old '$text$foo'
11940b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      split_by_wbr_list = wbr_filter(navigable_string).split("<wbr>")
11950b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      for (split_string, last) in enumerate_with_last(split_by_wbr_list):
11960b080452cca90f215d10d636abfb47701d7518daIgor Murashkin          navigable_string.insert_before(split_string)
11970b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
11980b080452cca90f215d10d636abfb47701d7518daIgor Murashkin          if not last:
11990b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            # Note that 'insert' will move existing tags to this spot
12000b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            # so make a new tag instead
12010b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            navigable_string.insert_before(wbr_tag())
12020b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12030b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # Remove the old unmodified text
12040b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      navigable_string.extract()
12050b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12060b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  return soup.decode()
1207