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
37c9c2c6849c68ddb458d63b5f864ea76a8448a3d2Yin-Chia YehNDKDOC_IMAGE_SRC_METADATA="../" + IMAGE_SRC_METADATA
381dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
39da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin_context_buf = None
40da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
41da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef _is_sec_or_ins(x):
42da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return isinstance(x, metadata_model.Section) or    \
43da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin         isinstance(x, metadata_model.InnerNamespace)
44da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
45da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin##
46da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin## Metadata Helpers
47da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin##
48da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
49da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef find_all_sections(root):
50da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
51da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Find all descendants that are Section or InnerNamespace instances.
52da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
53da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
54da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    root: a Metadata instance
55da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
56da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
57da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A list of Section/InnerNamespace instances
58da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
59da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
60da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    These are known as "sections" in the generated C code.
61da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
62da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return root.find_all(_is_sec_or_ins)
63da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
64da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef find_parent_section(entry):
65da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
66da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Find the closest ancestor that is either a Section or InnerNamespace.
67da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
68da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
69da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    entry: an Entry or Clone node
70da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
71da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
72da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    An instance of Section or InnerNamespace
73da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
74da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return entry.find_parent_first(_is_sec_or_ins)
75da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
76da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# find uniquely named entries (w/o recursing through inner namespaces)
77da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef find_unique_entries(node):
78da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
79da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Find all uniquely named entries, without recursing through inner namespaces.
80da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
81da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
82da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    node: a Section or InnerNamespace instance
83da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
84da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Yields:
85da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A sequence of MergedEntry nodes representing an entry
86da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
87da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
88da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    This collapses multiple entries with the same fully qualified name into
89da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    one entry (e.g. if there are multiple entries in different kinds).
90da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
91da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  if not isinstance(node, metadata_model.Section) and    \
92da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin     not isinstance(node, metadata_model.InnerNamespace):
93da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      raise TypeError("expected node to be a Section or InnerNamespace")
94da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
95586c861e6dab3fdf48fc8440c719ad0b59d49d72Igor Murashkin  d = OrderedDict()
96da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  # remove the 'kinds' from the path between sec and the closest entries
97da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  # then search the immediate children of the search path
98da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  search_path = isinstance(node, metadata_model.Section) and node.kinds \
99da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin                or [node]
100da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  for i in search_path:
101da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      for entry in i.entries:
102da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin          d[entry.name] = entry
103da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
104da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  for k,v in d.iteritems():
105da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      yield v.merge()
106da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
107da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef path_name(node):
108da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
109da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Calculate a period-separated string path from the root to this element,
110da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  by joining the names of each node and excluding the Metadata/Kind nodes
111da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  from the path.
112da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
113da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
114da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    node: a Node instance
115da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
116da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
117da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A string path
118da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
119da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
120da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  isa = lambda x,y: isinstance(x, y)
121da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  fltr = lambda x: not isa(x, metadata_model.Metadata) and \
122da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin                   not isa(x, metadata_model.Kind)
123da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
124da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  path = node.find_parents(fltr)
125da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  path = list(path)
126da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  path.reverse()
127da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  path.append(node)
128da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
129da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return ".".join((i.name for i in path))
130da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
131ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yehdef ndk(name):
132ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  """
133ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  Return the NDK version of given name, which replace
134ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  the leading "android" to "acamera"
135ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh
136ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  Args:
137ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh    name: name string of an entry
138ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh
139ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  Returns:
140ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh    A NDK version name string of the input name
141ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  """
142ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  name_list = name.split(".")
143ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  if name_list[0] == "android":
144ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh    name_list[0] = "acamera"
145ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh  return ".".join(name_list)
146ea7662f32e3b6e6e74f0fedbd558b35784e6e219Yin-Chia Yeh
147c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yehdef protobuf_type(entry):
148c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  """
149c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Return the protocol buffer message type for input metadata entry.
150c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Only support types used by static metadata right now
151c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
152c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Returns:
153c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    A string of protocol buffer type. Ex: "optional int32" or "repeated RangeInt"
154c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  """
155c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  typeName = None
156c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  if entry.typedef is None:
157c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    typeName = entry.type
158c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  else:
159c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    typeName = entry.typedef.name
160c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
161c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  typename_to_protobuftype = {
162c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "rational"               : "Rational",
163c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "size"                   : "Size",
164c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "sizeF"                  : "SizeF",
165c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "rectangle"              : "Rect",
166c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "streamConfigurationMap" : "StreamConfigurations",
167c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "rangeInt"               : "RangeInt",
168c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "rangeLong"              : "RangeLong",
169c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "colorSpaceTransform"    : "ColorSpaceTransform",
170c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "blackLevelPattern"      : "BlackLevelPattern",
171c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "byte"                   : "int32", # protocol buffer don't support byte
172c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "boolean"                : "bool",
173c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "float"                  : "float",
174c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "double"                 : "double",
175c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "int32"                  : "int32",
176c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "int64"                  : "int64",
177c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    "enumList"               : "int32"
178c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  }
179c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
180c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  if typeName not in typename_to_protobuftype:
181c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    print >> sys.stderr,\
182c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh      "  ERROR: Could not find protocol buffer type for {%s} type {%s} typedef {%s}" % \
183c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh          (entry.name, entry.type, entry.typedef)
184c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
185c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  proto_type = typename_to_protobuftype[typeName]
186c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
187c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  prefix = "optional"
188c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  if entry.container == 'array':
189c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    has_variable_size = False
190c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    for size in entry.container_sizes:
191c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh      try:
192c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh        size_int = int(size)
193c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh      except ValueError:
194c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh        has_variable_size = True
195c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
196c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    if has_variable_size:
197c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh      prefix = "repeated"
198c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
199c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  return "%s %s" %(prefix, proto_type)
200c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
201c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
202c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yehdef protobuf_name(entry):
203c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  """
204c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Return the protocol buffer field name for input metadata entry
205c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
206c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  Returns:
207c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh    A string. Ex: "android_colorCorrection_availableAberrationModes"
208c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  """
209c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh  return entry.name.replace(".", "_")
210c9b27dd2a92203ee914485cbc52a4f583d763142Yin-Chia Yeh
211aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef has_descendants_with_enums(node):
212aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
213aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Determine whether or not the current node is or has any descendants with an
214aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Enum node.
215aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
216aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
217aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    node: a Node instance
218aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
219aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
220aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    True if it finds an Enum node in the subtree, False otherwise
221aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
222aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  return bool(node.find_first(lambda x: isinstance(x, metadata_model.Enum)))
223aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
224aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef get_children_by_throwing_away_kind(node, member='entries'):
225aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
226aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Get the children of this node by compressing the subtree together by removing
227aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  the kind and then combining any children nodes with the same name together.
228aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
229aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
230aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    node: An instance of Section, InnerNamespace, or Kind
231aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
232aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
233aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    An iterable over the combined children of the subtree of node,
234aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    as if the Kinds never existed.
235aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
236aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Remarks:
237aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    Not recursive. Call this function repeatedly on each child.
238aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
239aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
240aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if isinstance(node, metadata_model.Section):
241aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    # Note that this makes jump from Section to Kind,
242aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    # skipping the Kind entirely in the tree.
243aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    node_to_combine = node.combine_kinds_into_single_node()
244aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  else:
245aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    node_to_combine = node
246aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
247aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  combined_kind = node_to_combine.combine_children_by_name()
248aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
249aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  return (i for i in getattr(combined_kind, member))
250aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
251aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef get_children_by_filtering_kind(section, kind_name, member='entries'):
252aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
25323d4b2c087bd4286bf16bda83b6d9f72c5bb1718Eino-Ville Talvala  Takes a section and yields the children of the merged kind under this section.
254aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
255aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
256aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    section: An instance of Section
257aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    kind_name: A name of the kind, i.e. 'dynamic' or 'static' or 'controls'
258aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
259aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
26023d4b2c087bd4286bf16bda83b6d9f72c5bb1718Eino-Ville Talvala    An iterable over the children of the specified merged kind.
261aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
262aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
26323d4b2c087bd4286bf16bda83b6d9f72c5bb1718Eino-Ville Talvala  matched_kind = next((i for i in section.merged_kinds if i.name == kind_name), None)
264aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
265aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if matched_kind:
266aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return getattr(matched_kind, member)
267aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  else:
268aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return ()
269aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
270da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin##
271da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin## Filters
272da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin##
273da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
274da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# abcDef.xyz -> ABC_DEF_XYZ
275da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef csym(name):
276da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
277da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Convert an entry name string into an uppercase C symbol.
278da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
279da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
280da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A string
281da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
282da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
283da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    csym('abcDef.xyz') == 'ABC_DEF_XYZ'
284da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
285da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  newstr = name
286da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  newstr = "".join([i.isupper() and ("_" + i) or i for i in newstr]).upper()
287da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  newstr = newstr.replace(".", "_")
288da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return newstr
289da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
290da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# abcDef.xyz -> abc_def_xyz
291da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef csyml(name):
292da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
293da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Convert an entry name string into a lowercase C symbol.
294da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
295da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
296da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A string
297da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
298da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
299da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    csyml('abcDef.xyz') == 'abc_def_xyz'
300da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
301da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return csym(name).lower()
302da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
303da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# pad with spaces to make string len == size. add new line if too big
304da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef ljust(size, indent=4):
305da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
306da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Creates a function that given a string will pad it with spaces to make
307da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  the string length == size. Adds a new line if the string was too big.
308da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
309da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
310da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    size: an integer representing how much spacing should be added
311da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    indent: an integer representing the initial indendation level
312da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
313da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
314da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A function that takes a string and returns a string.
315da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
316da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
317da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    ljust(8)("hello") == 'hello   '
318da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
319da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
320da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    Deprecated. Use pad instead since it works for non-first items in a
321da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    Mako template.
322da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
323da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  def inner(what):
324da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    newstr = what.ljust(size)
325da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    if len(newstr) > size:
326da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      return what + "\n" + "".ljust(indent + size)
327da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    else:
328da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      return newstr
329da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return inner
330da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
331da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef _find_new_line():
332da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
333da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  if _context_buf is None:
334da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    raise ValueError("Context buffer was not set")
335da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
336da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  buf = _context_buf
337da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  x = -1 # since the first read is always ''
338da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  cur_pos = buf.tell()
339da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  while buf.tell() > 0 and buf.read(1) != '\n':
340da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    buf.seek(cur_pos - x)
341da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    x = x + 1
342da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
343da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  buf.seek(cur_pos)
344da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
345da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return int(x)
346da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
347da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# Pad the string until the buffer reaches the desired column.
348da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# If string is too long, insert a new line with 'col' spaces instead
349da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef pad(col):
350da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
351da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Create a function that given a string will pad it to the specified column col.
352da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  If the string overflows the column, put the string on a new line and pad it.
353da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
354da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
355da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    col: an integer specifying the column number
356da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
357da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
358da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A function that given a string will produce a padded string.
359da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
360da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
361da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    pad(8)("hello") == 'hello   '
362da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
363da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
364da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    This keeps track of the line written by Mako so far, so it will always
365da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    align to the column number correctly.
366da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
367da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  def inner(what):
368da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    wut = int(col)
369da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    current_col = _find_new_line()
370da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
371da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    if len(what) > wut - current_col:
372da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      return what + "\n".ljust(col)
373da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    else:
374da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin      return what.ljust(wut - current_col)
375da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return inner
376da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
377da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin# int32 -> TYPE_INT32, byte -> TYPE_BYTE, etc. note that enum -> TYPE_INT32
378da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkindef ctype_enum(what):
379da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
380da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Generate a camera_metadata_type_t symbol from a type string.
381da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
382da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Args:
383da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    what: a type string
384da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
385da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Returns:
386da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    A string representing the camera_metadata_type_t
387da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
388da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Example:
389da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    ctype_enum('int32') == 'TYPE_INT32'
390da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    ctype_enum('int64') == 'TYPE_INT64'
391da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    ctype_enum('float') == 'TYPE_FLOAT'
392da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin
393da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  Remarks:
394e6b664671e35984156e06e17531311a09864ac8bIgor Murashkin    An enum is coerced to a byte since the rest of the camera_metadata
395da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin    code doesn't support enums directly yet.
396da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  """
397da1c314e080d33eb6b93a1d3da070c99b41e7b22Igor Murashkin  return 'TYPE_%s' %(what.upper())
398aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
399b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
400b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin# Calculate a java type name from an entry with a Typedef node
401b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef _jtypedef_type(entry):
402b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  typedef = entry.typedef
403b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  additional = ''
404b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
405b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # Hacky way to deal with arrays. Assume that if we have
406b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # size 'Constant x N' the Constant is part of the Typedef size.
407b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # So something sized just 'Constant', 'Constant1 x Constant2', etc
408b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # is not treated as a real java array.
409b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  if entry.container == 'array':
410b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    has_variable_size = False
411b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    for size in entry.container_sizes:
412b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      try:
413b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        size_int = int(size)
414b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      except ValueError:
415b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        has_variable_size = True
416b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
417b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    if has_variable_size:
418b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      additional = '[]'
419b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
420b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  try:
421b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    name = typedef.languages['java']
422b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
423b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    return "%s%s" %(name, additional)
424b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  except KeyError:
425b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    return None
426b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
427b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin# Box if primitive. Otherwise leave unboxed.
428b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef _jtype_box(type_name):
429b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  mapping = {
430b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'boolean': 'Boolean',
431b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'byte': 'Byte',
432b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'int': 'Integer',
433b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'float': 'Float',
434b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'double': 'Double',
435b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    'long': 'Long'
436b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  }
437b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
438b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  return mapping.get(type_name, type_name)
439b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
440b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef jtype_unboxed(entry):
441aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
442b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  Calculate the Java type from an entry type string, to be used whenever we
443b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  need the regular type in Java. It's not boxed, so it can't be used as a
444b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  generic type argument when the entry type happens to resolve to a primitive.
445aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
446aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Remarks:
447aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    Since Java generics cannot be instantiated with primitives, this version
448b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    is not applicable in that case. Use jtype_boxed instead for that.
449aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
450aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
451aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    The string representing the Java type.
452aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
453aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if not isinstance(entry, metadata_model.Entry):
454aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    raise ValueError("Expected entry to be an instance of Entry")
455aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
456b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  metadata_type = entry.type
457aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
458b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  java_type = None
459aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
460b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  if entry.typedef:
461b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    typedef_name = _jtypedef_type(entry)
462b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    if typedef_name:
463b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      java_type = typedef_name # already takes into account arrays
464aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
465b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  if not java_type:
466e04dbd44c7d2043c31107ff3f9efdad71f438071Zhijun He    if not java_type and entry.enum and metadata_type == 'byte':
467e04dbd44c7d2043c31107ff3f9efdad71f438071Zhijun He      # Always map byte enums to Java ints, unless there's a typedef override
468d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala      base_type = 'int'
469aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
470b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    else:
471b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      mapping = {
472b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'int32': 'int',
473b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'int64': 'long',
474b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'float': 'float',
475b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'double': 'double',
476b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'byte': 'byte',
477b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin        'rational': 'Rational'
478b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      }
479b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
480b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      base_type = mapping[metadata_type]
481b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
482b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    # Convert to array (enums, basic types)
483b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    if entry.container == 'array':
484b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      additional = '[]'
485b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    else:
486b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin      additional = ''
487b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
488b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    java_type = '%s%s' %(base_type, additional)
489b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
490b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  # Now box this sucker.
491b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  return java_type
492b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
493b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef jtype_boxed(entry):
494b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  """
495b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  Calculate the Java type from an entry type string, to be used as a generic
496b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  type argument in Java. The type is guaranteed to inherit from Object.
497b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
498b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  It will only box when absolutely necessary, i.e. int -> Integer[], but
499b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  int[] -> int[].
500aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
501b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  Remarks:
502b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    Since Java generics cannot be instantiated with primitives, this version
503b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    will use boxed types when absolutely required.
504aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
505b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  Returns:
506b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin    The string representing the boxed Java type.
507b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  """
508b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  unboxed_type = jtype_unboxed(entry)
509b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  return _jtype_box(unboxed_type)
510b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin
51135a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkindef _is_jtype_generic(entry):
51235a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  """
51335a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Determine whether or not the Java type represented by the entry type
51435a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  string and/or typedef is a Java generic.
51535a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
51635a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  For example, "Range<Integer>" would be considered a generic, whereas
51735a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  a "MeteringRectangle" or a plain "Integer" would not be considered a generic.
51835a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
51935a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Args:
52035a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    entry: An instance of an Entry node
52135a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
52235a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Returns:
52335a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    True if it's a java generic, False otherwise.
52435a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  """
52535a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  if entry.typedef:
52635a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    local_typedef = _jtypedef_type(entry)
52735a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    if local_typedef:
52835a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin      match = re.search(r'<.*>', local_typedef)
52935a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin      return bool(match)
53035a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  return False
53135a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
532b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkindef _jtype_primitive(what):
533aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
534aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Calculate the Java type from an entry type string.
535aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
536aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Remarks:
537aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    Makes a special exception for Rational, since it's a primitive in terms of
538aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    the C-library camera_metadata type system.
539aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
540aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
541aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    The string representing the primitive type
542aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
543aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  mapping = {
544aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'int32': 'int',
545aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'int64': 'long',
546aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'float': 'float',
547aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'double': 'double',
548aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'byte': 'byte',
549aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    'rational': 'Rational'
550aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  }
551aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
552aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  try:
553aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return mapping[what]
554aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  except KeyError as e:
555aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    raise ValueError("Can't map '%s' to a primitive, not supported" %what)
556aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
557aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef jclass(entry):
558aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
559aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Calculate the java Class reference string for an entry.
560aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
561aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
562aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    entry: an Entry node
563aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
564aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Example:
565aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    <entry name="some_int" type="int32"/>
566aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    <entry name="some_int_array" type="int32" container='array'/>
567aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
568aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    jclass(some_int) == 'int.class'
569aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    jclass(some_int_array) == 'int[].class'
570aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
571aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
572aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    The ClassName.class string
573aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
574aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
575b8dc88148bca2e5a267c2ff39aff94b98b00ad6dIgor Murashkin  return "%s.class" %jtype_unboxed(entry)
576aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
57735a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkindef jkey_type_token(entry):
57835a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  """
57935a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Calculate the java type token compatible with a Key constructor.
58035a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  This will be the Java Class<T> for non-generic classes, and a
58135a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  TypeReference<T> for generic classes.
58235a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
58335a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Args:
58435a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    entry: An entry node
58535a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
58635a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  Returns:
58735a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    The ClassName.class string, or 'new TypeReference<ClassName>() {{ }}' string
58835a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  """
58935a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  if _is_jtype_generic(entry):
59035a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    return "new TypeReference<%s>() {{ }}" %(jtype_boxed(entry))
59135a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin  else:
59235a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin    return jclass(entry)
59335a108fbfe7d174682187fa6a87f0590837924d0Igor Murashkin
594aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef jidentifier(what):
595aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
596aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Convert the input string into a valid Java identifier.
597aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
598aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
599aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    what: any identifier string
600aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
601aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
602aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    String with added underscores if necessary.
603aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
604aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if re.match("\d", what):
605aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return "_%s" %what
606aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  else:
607aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return what
608aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
609aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef enum_calculate_value_string(enum_value):
610aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
611aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Calculate the value of the enum, even if it does not have one explicitly
612aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  defined.
613aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
614aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  This looks back for the first enum value that has a predefined value and then
615aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  applies addition until we get the right value, using C-enum semantics.
616aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
617aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
618aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    enum_value: an EnumValue node with a valid Enum parent
619aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
620aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Example:
621aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    <enum>
622aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      <value>X</value>
623aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      <value id="5">Y</value>
624aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      <value>Z</value>
625aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    </enum>
626aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
627aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    enum_calculate_value_string(X) == '0'
628aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    enum_calculate_Value_string(Y) == '5'
629aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    enum_calculate_value_string(Z) == '6'
630aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
631aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
632aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    String that represents the enum value as an integer literal.
633aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
634aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
635aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  enum_value_siblings = list(enum_value.parent.values)
636aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  this_index = enum_value_siblings.index(enum_value)
637aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
638aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  def is_hex_string(instr):
639aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return bool(re.match('0x[a-f0-9]+$', instr, re.IGNORECASE))
640aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
641aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  base_value = 0
642aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  base_offset = 0
643aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  emit_as_hex = False
644aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
645aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  this_id = enum_value_siblings[this_index].id
646aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  while this_index != 0 and not this_id:
647aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    this_index -= 1
648aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    base_offset += 1
649aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    this_id = enum_value_siblings[this_index].id
650aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
651aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if this_id:
652aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    base_value = int(this_id, 0)  # guess base
653aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    emit_as_hex = is_hex_string(this_id)
654aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
655aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  if emit_as_hex:
656aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return "0x%X" %(base_value + base_offset)
657aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  else:
658aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    return "%d" %(base_value + base_offset)
659aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
660aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef enumerate_with_last(iterable):
661aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
662aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Enumerate a sequence of iterable, while knowing if this element is the last in
663aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  the sequence or not.
664aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
665aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
666aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    iterable: an Iterable of some sequence
667aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
668aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Yields:
669aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    (element, bool) where the bool is True iff the element is last in the seq.
670aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
671aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  it = (i for i in iterable)
672aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
673aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  first = next(it)  # OK: raises exception if it is empty
674aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
675aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  second = first  # for when we have only 1 element in iterable
676aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
677aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  try:
678aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    while True:
679aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      second = next(it)
680aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      # more elements remaining.
681aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      yield (first, False)
682aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin      first = second
683aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  except StopIteration:
684aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    # last element. no more elements left
685aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    yield (second, True)
686aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
687aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkindef pascal_case(what):
688aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
689aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Convert the first letter of a string to uppercase, to make the identifier
690aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  conform to PascalCase.
691aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
6925250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  If there are dots, remove the dots, and capitalize the letter following
6935250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  where the dot was. Letters that weren't following dots are left unchanged,
6945250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  except for the first letter of the string (which is made upper-case).
6955250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
696aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
697aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    what: a string representing some identifier
698aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
699aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
700aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    String with first letter capitalized
701aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
702aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Example:
703aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    pascal_case("helloWorld") == "HelloWorld"
704aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    pascal_case("foo") == "Foo"
7055250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    pascal_case("hello.world") = "HelloWorld"
7065250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    pascal_case("fooBar.fooBar") = "FooBarFooBar"
7075250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  """
7085250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  return "".join([s[0:1].upper() + s[1:] for s in what.split('.')])
7095250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
7105250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knightdef jkey_identifier(what):
7115250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  """
7125250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  Return a Java identifier from a property name.
7135250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
7145250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  Args:
7155250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    what: a string representing a property name.
7165250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
7175250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  Returns:
7185250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    Java identifier corresponding to the property name. May need to be
7195250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    prepended with the appropriate Java class name by the caller of this
7205250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    function. Note that the outer namespace is stripped from the property
7215250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight    name.
7225250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight
7235250aa1d42dea0773ab98fc0b2cd3f172067c050Timothy Knight  Example:
724d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    jkey_identifier("android.lens.facing") == "LENS_FACING"
725aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
726d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  return csym(what[what.find('.') + 1:])
727aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
728d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvaladef jenum_value(enum_entry, enum_value):
729aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
730d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  Calculate the Java name for an integer enum value
731aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
732aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Args:
733d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    enum: An enum-typed Entry node
734d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    value: An EnumValue node for the enum
735aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
736aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  Returns:
737aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin    String representing the Java symbol
738aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin  """
739aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
740d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  cname = csym(enum_entry.name)
741d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  return cname[cname.find('_') + 1:] + '_' + enum_value.name
742d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
743567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvaladef generate_extra_javadoc_detail(entry):
744567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  """
745567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  Returns a function to add extra details for an entry into a string for inclusion into
746567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  javadoc. Adds information about units, the list of enum values for this key, and the valid
747567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  range.
748567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  """
749567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  def inner(text):
750567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.units:
751567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '\n\n<b>Units</b>: %s\n' % (dedent(entry.units))
752567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.enum and not (entry.typedef and entry.typedef.languages.get('java')):
753567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '\n\n<b>Possible values:</b>\n<ul>\n'
754567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      for value in entry.enum.values:
755567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        if not value.hidden:
756567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala          text += '  <li>{@link #%s %s}</li>\n' % ( jenum_value(entry, value ), value.name )
757567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '</ul>\n'
758567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.range:
759567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      if entry.enum and not (entry.typedef and entry.typedef.languages.get('java')):
760567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        text += '\n\n<b>Available values for this device:</b><br>\n'
761567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      else:
762567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        text += '\n\n<b>Range of valid values:</b><br>\n'
763567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '%s\n' % (dedent(entry.range))
764567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.hwlevel != 'legacy': # covers any of (None, 'limited', 'full')
765567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += '\n\n<b>Optional</b> - This value may be {@code null} on some devices.\n'
766567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.hwlevel == 'full':
767567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += \
768567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        '\n<b>Full capability</b> - \n' + \
769567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        'Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the\n' + \
770567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        'android.info.supportedHardwareLevel key\n'
771567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.hwlevel == 'limited':
772567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += \
773567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        '\n<b>Limited capability</b> - \n' + \
774567167ac6be36e732e98089d6e5d7d4f041f3323Eino-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' + \
775567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala        'android.info.supportedHardwareLevel key\n'
776567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    if entry.hwlevel == 'legacy':
777567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      text += "\nThis key is available on all devices."
778567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala
779567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala    return text
780567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala  return inner
781567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala
782567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala
78363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvaladef javadoc(metadata, indent = 4):
784d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  """
78563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala  Returns a function to format a markdown syntax text block as a
78663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala  javadoc comment section, given a set of metadata
787d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
788d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  Args:
78963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    metadata: A Metadata instance, representing the the top-level root
79063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      of the metadata for cross-referencing
791d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    indent: baseline level of indentation for javadoc block
792d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  Returns:
79363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    A function that transforms a String text block as follows:
794d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    - Indent and * for insertion into a Javadoc comment block
7958aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    - Trailing whitespace removed
7968aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    - Entire body rendered via markdown to generate HTML
79763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    - All tag names converted to appropriate Javadoc {@link} with @see
79863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      for each tag
799d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
800d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  Example:
801d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "This is a comment for Javadoc\n" +
802d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "     with multiple lines, that should be   \n" +
803d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "     formatted better\n" +
804d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "\n" +
805d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "    That covers multiple lines as well\n"
80663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    "    And references android.control.mode\n"
807d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
808d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    transforms to
8098aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    "    * <p>This is a comment for Javadoc\n" +
810d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala    "    * with multiple lines, that should be\n" +
8118aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    "    * formatted better</p>\n" +
8128aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    "    * <p>That covers multiple lines as well</p>\n" +
81363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    "    * and references {@link CaptureRequest#CONTROL_MODE android.control.mode}\n" +
81463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    "    *\n" +
81563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    "    * @see CaptureRequest#CONTROL_MODE\n"
816d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala  """
81763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala  def javadoc_formatter(text):
81863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    comment_prefix = " " * indent + " * ";
819d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
82063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # render with markdown => HTML
82163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    javatext = md(text, JAVADOC_IMAGE_SRC_METADATA)
822d4e240adc06f10372f6e18b8ed23e14c4a4138d9Eino-Ville Talvala
823ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    # Identity transform for javadoc links
824ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    def javadoc_link_filter(target, shortname):
825ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return '{@link %s %s}' % (target, shortname)
826ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
827ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    javatext = filter_links(javatext, javadoc_link_filter)
828ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
82963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # Crossref tag names
83063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    kind_mapping = {
83163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        'static': 'CameraCharacteristics',
83263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        'dynamic': 'CaptureResult',
83363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        'controls': 'CaptureRequest' }
834aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
83563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # Convert metadata entry "android.x.y.z" to form
83663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # "{@link CaptureRequest#X_Y_Z android.x.y.z}"
83763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    def javadoc_crossref_filter(node):
838c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh      if node.applied_visibility in ('public', 'java_public'):
83963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        return '{@link %s#%s %s}' % (kind_mapping[node.kind],
84063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala                                     jkey_identifier(node.name),
84163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala                                     node.name)
84263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      else:
84363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        return node.name
844aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
84563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # For each public tag "android.x.y.z" referenced, add a
84663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # "@see CaptureRequest#X_Y_Z"
847ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    def javadoc_crossref_see_filter(node_set):
848c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh      node_set = (x for x in node_set if x.applied_visibility in ('public', 'java_public'))
84963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
85063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      text = '\n'
85163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      for node in node_set:
85263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        text = text + '\n@see %s#%s' % (kind_mapping[node.kind],
85363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala                                      jkey_identifier(node.name))
85463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
85563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      return text if text != '\n' else ''
85663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
857ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    javatext = filter_tags(javatext, metadata, javadoc_crossref_filter, javadoc_crossref_see_filter)
85863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
85963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    def line_filter(line):
86063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      # Indent each line
86163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      # Add ' * ' to it for stylistic reasons
86263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      # Strip right side of trailing whitespace
863567167ac6be36e732e98089d6e5d7d4f041f3323Eino-Ville Talvala      return (comment_prefix + line).rstrip()
86463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
86563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # Process each line with above filter
86663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    javatext = "\n".join(line_filter(i) for i in javatext.split("\n")) + "\n"
86763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
86863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    return javatext
86963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
87063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala  return javadoc_formatter
871aa133d352a42aebf93320eded40c75b4d7cff6e7Igor Murashkin
872d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yehdef ndkdoc(metadata, indent = 4):
873d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh  """
874d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh  Returns a function to format a markdown syntax text block as a
875d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh  NDK camera API C/C++ comment section, given a set of metadata
876d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
877d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh  Args:
878d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    metadata: A Metadata instance, representing the the top-level root
879d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      of the metadata for cross-referencing
880d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    indent: baseline level of indentation for comment block
881d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh  Returns:
882d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    A function that transforms a String text block as follows:
883d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    - Indent and * for insertion into a comment block
884d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    - Trailing whitespace removed
885d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    - Entire body rendered via markdown
886d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    - All tag names converted to appropriate NDK tag name for each tag
887d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
888d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh  Example:
889d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "This is a comment for NDK\n" +
890d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "     with multiple lines, that should be   \n" +
891d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "     formatted better\n" +
892d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "\n" +
893d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "    That covers multiple lines as well\n"
894d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "    And references android.control.mode\n"
895d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
896d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    transforms to
897d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "    * This is a comment for NDK\n" +
898d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "    * with multiple lines, that should be\n" +
899d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "    * formatted better\n" +
900d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "    * That covers multiple lines as well\n" +
901d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "    * and references ACAMERA_CONTROL_MODE\n" +
902d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "    *\n" +
903d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "    * @see ACAMERA_CONTROL_MODE\n"
904d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh  """
905d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh  def ndkdoc_formatter(text):
906d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    # render with markdown => HTML
907c9c2c6849c68ddb458d63b5f864ea76a8448a3d2Yin-Chia Yeh    ndktext = md(text, NDKDOC_IMAGE_SRC_METADATA, False)
908d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
909d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    # Convert metadata entry "android.x.y.z" to form
910d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    # NDK tag format of "ACAMERA_X_Y_Z"
911d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    def ndkdoc_crossref_filter(node):
912d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      if node.applied_ndk_visible == 'true':
913d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh        return csym(ndk(node.name))
914d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      else:
915d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh        return node.name
916d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
917d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    # For each public tag "android.x.y.z" referenced, add a
918d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    # "@see ACAMERA_X_Y_Z"
919d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    def ndkdoc_crossref_see_filter(node_set):
920d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      node_set = (x for x in node_set if x.applied_ndk_visible == 'true')
921d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
922d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      text = '\n'
923d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      for node in node_set:
924d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh        text = text + '\n@see %s' % (csym(ndk(node.name)))
925d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
926d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      return text if text != '\n' else ''
927d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
928d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    ndktext = filter_tags(ndktext, metadata, ndkdoc_crossref_filter, ndkdoc_crossref_see_filter)
929d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
930d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    ndktext = ndk_replace_tag_wildcards(ndktext, metadata)
931d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
932d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    comment_prefix = " " * indent + " * ";
933d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
934d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    def line_filter(line):
935d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      # Indent each line
936d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      # Add ' * ' to it for stylistic reasons
937d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      # Strip right side of trailing whitespace
938d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      return (comment_prefix + line).rstrip()
939d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
940d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    # Process each line with above filter
941d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    ndktext = "\n".join(line_filter(i) for i in ndktext.split("\n")) + "\n"
942d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
943d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    return ndktext
944d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
945d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh  return ndkdoc_formatter
946d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
94788b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkindef dedent(text):
94888b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  """
94988b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  Remove all common indentation from every line but the 0th.
95088b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  This will avoid getting <code> blocks when rendering text via markdown.
95188b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  Ignoring the 0th line will also allow the 0th line not to be aligned.
95288b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
95388b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  Args:
95488b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    text: A string of text to dedent.
95588b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
95688b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  Returns:
95788b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    String dedented by above rules.
95888b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
95988b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  For example:
96088b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    assertEquals("bar\nline1\nline2",   dedent("bar\n  line1\n  line2"))
96188b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    assertEquals("bar\nline1\nline2",   dedent(" bar\n  line1\n  line2"))
96288b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    assertEquals("bar\n  line1\nline2", dedent(" bar\n    line1\n  line2"))
96388b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  """
96488b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text = textwrap.dedent(text)
96588b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text_lines = text.split('\n')
96688b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text_not_first = "\n".join(text_lines[1:])
96788b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text_not_first = textwrap.dedent(text_not_first)
96888b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  text = text_lines[0] + "\n" + text_not_first
96988b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
97088b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin  return text
97188b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin
972c9c2c6849c68ddb458d63b5f864ea76a8448a3d2Yin-Chia Yehdef md(text, img_src_prefix="", table_ext=True):
9738aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    """
9748aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    Run text through markdown to produce HTML.
9758aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9768aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    This also removes all common indentation from every line but the 0th.
9778aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    This will avoid getting <code> blocks in markdown.
9788aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    Ignoring the 0th line will also allow the 0th line not to be aligned.
9798aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9801dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    Args:
9811dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin      text: A markdown-syntax using block of text to format.
9821dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin      img_src_prefix: An optional string to prepend to each <img src="target"/>
9831dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
9841dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    Returns:
9851dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin      String rendered by markdown and other rules applied (see above).
9861dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
9878aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    For example, this avoids the following situation:
9888aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9898aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!-- Input -->
9908aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9918aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!--- can't use dedent directly since 'foo' has no indent -->
9928aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <notes>foo
9938aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin          bar
9948aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin          bar
9958aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      </notes>
9968aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
9978aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!-- Bad Output -- >
9988aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!-- if no dedent is done generated code looks like -->
9998aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <p>foo
10008aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin        <code><pre>
10018aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin          bar
10028aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin          bar</pre></code>
10038aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      </p>
10048aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
10058aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    Instead we get the more natural expected result:
10068aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
10078aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <!-- Good Output -->
10088aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      <p>foo
10098aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      bar
10108aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin      bar</p>
10118aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
10128aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    """
101388b858d5e4db3eb66fe570647626a592ebb6af91Igor Murashkin    text = dedent(text)
10141dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
1015a48441daa766098190b40d5187ce1963d8a980afIgor Murashkin    # full list of extensions at http://pythonhosted.org/Markdown/extensions/
1016c9c2c6849c68ddb458d63b5f864ea76a8448a3d2Yin-Chia Yeh    md_extensions = ['tables'] if table_ext else []# make <table> with ASCII |_| tables
10178aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin    # render with markdown
1018a48441daa766098190b40d5187ce1963d8a980afIgor Murashkin    text = markdown.markdown(text, md_extensions)
10191dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin
10201dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    # prepend a prefix to each <img src="foo"> -> <img src="${prefix}foo">
10211dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    text = re.sub(r'src="([^"]*)"', 'src="' + img_src_prefix + r'\1"', text)
10221dd4ecb0ea0589610b3616459b707c2898889153Igor Murashkin    return text
10238aa2a11bbff97d9789bb06cdc2e28dadb6c5926aIgor Murashkin
102463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvaladef filter_tags(text, metadata, filter_function, summary_function = None):
102563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    """
102663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    Find all references to tags in the form outer_namespace.xxx.yyy[.zzz] in
102763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    the provided text, and pass them through filter_function and summary_function.
102863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
102963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    Used to linkify entry names in HMTL, javadoc output.
103063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
103163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    Args:
103263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      text: A string representing a block of text destined for output
103363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      metadata: A Metadata instance, the root of the metadata properties tree
103463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      filter_function: A Node->string function to apply to each node
103563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        when found in text; the string returned replaces the tag name in text.
103650f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala      summary_function: A Node list->string function that is provided the list of
103750f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala        unique tag nodes found in text, and which must return a string that is
103850f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala        then appended to the end of the text. The list is sorted alphabetically
103950f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala        by node name.
104063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    """
104163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
104263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    tag_set = set()
104363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    def name_match(name):
104463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      return lambda node: node.name == name
104563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
104663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # Match outer_namespace.x.y or outer_namespace.x.y.z, making sure
104763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    # to grab .z and not just outer_namespace.x.y.  (sloppy, but since we
1048ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    # check for validity, a few false positives don't hurt).
1049ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    # Try to ignore items of the form {@link <outer_namespace>...
105063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    for outer_namespace in metadata.outer_namespaces:
105163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
1052ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      tag_match = r"(?<!\{@link\s)" + outer_namespace.name + \
10537fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        r"\.([a-zA-Z0-9\n]+)\.([a-zA-Z0-9\n]+)(\.[a-zA-Z0-9\n]+)?([/]?)"
105463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
105563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      def filter_sub(match):
105663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        whole_match = match.group(0)
105763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        section1 = match.group(1)
105863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        section2 = match.group(2)
105963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        section3 = match.group(3)
10607fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        end_slash = match.group(4)
10617fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
10627fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        # Don't linkify things ending in slash (urls, for example)
10637fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        if end_slash:
10647fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          return whole_match
10657fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
10667fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        candidate = ""
106763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
106863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        # First try a two-level match
10697fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        candidate2 = "%s.%s.%s" % (outer_namespace.name, section1, section2)
107063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        got_two_level = False
107163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
10727fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        node = metadata.find_first(name_match(candidate2.replace('\n','')))
10737fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        if not node and '\n' in section2:
10747fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          # Linefeeds are ambiguous - was the intent to add a space,
10757fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          # or continue a lengthy name? Try the former now.
10767fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          candidate2b = "%s.%s.%s" % (outer_namespace.name, section1, section2[:section2.find('\n')])
10777fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          node = metadata.find_first(name_match(candidate2b))
10787fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          if node:
10797fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            candidate2 = candidate2b
108063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
108163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        if node:
10827fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          # Have two-level match
108363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          got_two_level = True
10847fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          candidate = candidate2
10857fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        elif section3:
10867fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          # Try three-level match
10877fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          candidate3 = "%s%s" % (candidate2, section3)
10887fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          node = metadata.find_first(name_match(candidate3.replace('\n','')))
10897fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
10907fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          if not node and '\n' in section3:
10917fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            # Linefeeds are ambiguous - was the intent to add a space,
10927fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            # or continue a lengthy name? Try the former now.
10937fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            candidate3b = "%s%s" % (candidate2, section3[:section3.find('\n')])
10947fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            node = metadata.find_first(name_match(candidate3b))
10957fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            if node:
10967fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala              candidate3 = candidate3b
10977fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
10987fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala          if node:
10997fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            # Have 3-level match
11007fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala            candidate = candidate3
11017fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala
11027fabc1e635ee4213c8414c24f621e55a6aece1f1Eino-Ville Talvala        # Replace match with crossref or complain if a likely match couldn't be matched
110363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
110463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        if node:
110563c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          tag_set.add(node)
110663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          return whole_match.replace(candidate,filter_function(node))
110763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala        else:
110863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          print >> sys.stderr,\
110963c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala            "  WARNING: Could not crossref likely reference {%s}" % (match.group(0))
111063c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala          return whole_match
111163c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
111263c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      text = re.sub(tag_match, filter_sub, text)
111363c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
111463c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    if summary_function is not None:
111550f45c4d120ea1ad00017e5b850ed5bcf3261efeEino-Ville Talvala      return text + summary_function(sorted(tag_set, key=lambda x: x.name))
111663c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala    else:
111763c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala      return text
111863c0fb27d923a32d9a398471ad318bfe84befbebEino-Ville Talvala
1119d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yehdef ndk_replace_tag_wildcards(text, metadata):
1120d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    """
1121d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    Find all references to tags in the form android.xxx.* or android.xxx.yyy.*
1122d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    in the provided text, and replace them by NDK format of "ACAMERA_XXX_*" or
1123d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    "ACAMERA_XXX_YYY_*"
1124d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
1125d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    Args:
1126d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      text: A string representing a block of text destined for output
1127d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      metadata: A Metadata instance, the root of the metadata properties tree
1128d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    """
1129d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    tag_match = r"android\.([a-zA-Z0-9\n]+)\.\*"
1130d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    tag_match_2 = r"android\.([a-zA-Z0-9\n]+)\.([a-zA-Z0-9\n]+)\*"
1131d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
1132d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    def filter_sub(match):
1133d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      return "ACAMERA_" + match.group(1).upper() + "_*"
1134d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    def filter_sub_2(match):
1135d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh      return "ACAMERA_" + match.group(1).upper() + match.group(2).upper() + "_*"
1136d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
1137d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    text = re.sub(tag_match, filter_sub, text)
1138d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    text = re.sub(tag_match_2, filter_sub_2, text)
1139d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh    return text
1140d4eae97e331b077b30ba07463b61bc766f924143Yin-Chia Yeh
1141ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvaladef filter_links(text, filter_function, summary_function = None):
1142ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    """
1143ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    Find all references to tags in the form {@link xxx#yyy [zzz]} in the
1144ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    provided text, and pass them through filter_function and
1145ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    summary_function.
1146ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1147ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    Used to linkify documentation cross-references in HMTL, javadoc output.
1148ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1149ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    Args:
1150ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      text: A string representing a block of text destined for output
1151ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      metadata: A Metadata instance, the root of the metadata properties tree
1152ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      filter_function: A (string, string)->string function to apply to each 'xxx#yyy',
1153ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala        zzz pair when found in text; the string returned replaces the tag name in text.
1154ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      summary_function: A string list->string function that is provided the list of
1155ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala        unique targets found in text, and which must return a string that is
1156ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala        then appended to the end of the text. The list is sorted alphabetically
1157ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala        by node name.
1158ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1159ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    """
1160ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1161ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    target_set = set()
1162ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    def name_match(name):
1163ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return lambda node: node.name == name
1164ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1165ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    tag_match = r"\{@link\s+([^\s\}]+)([^\}]*)\}"
1166ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1167ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    def filter_sub(match):
1168ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      whole_match = match.group(0)
1169ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      target = match.group(1)
1170ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      shortname = match.group(2).strip()
1171ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1172ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      #print "Found link '%s' as '%s' -> '%s'" % (target, shortname, filter_function(target, shortname))
1173ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1174ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      # Replace match with crossref
1175ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      target_set.add(target)
1176ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return filter_function(target, shortname)
1177ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1178ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    text = re.sub(tag_match, filter_sub, text)
1179ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1180ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    if summary_function is not None:
1181ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return text + summary_function(sorted(target_set))
1182ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala    else:
1183ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala      return text
1184ddda2bb917a1eb725c85d8c2b61bff2abf568a95Eino-Ville Talvala
1185f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvaladef any_visible(section, kind_name, visibilities):
1186f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  """
1187f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Determine if entries in this section have an applied visibility that's in
1188f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  the list of given visibilities.
1189f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1190f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Args:
1191f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    section: A section of metadata
1192f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    kind_name: A name of the kind, i.e. 'dynamic' or 'static' or 'controls'
1193f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    visibilities: An iterable of visibilities to match against
1194f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1195f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Returns:
1196f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    True if the section has any entries with any of the given visibilities. False otherwise.
1197f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  """
1198f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1199f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  for inner_namespace in get_children_by_filtering_kind(section, kind_name,
1200f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala                                                        'namespaces'):
1201f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    if any(filter_visibility(inner_namespace.merged_entries, visibilities)):
1202f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala      return True
1203f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1204f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  return any(filter_visibility(get_children_by_filtering_kind(section, kind_name,
1205f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala                                                              'merged_entries'),
1206f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala                               visibilities))
1207f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1208f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1209f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvaladef filter_visibility(entries, visibilities):
1210f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  """
1211f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Remove entries whose applied visibility is not in the supplied visibilities.
1212f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1213f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Args:
1214f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    entries: An iterable of Entry nodes
1215f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    visibilities: An iterable of visibilities to filter against
1216f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala
1217f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  Yields:
1218f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala    An iterable of Entry nodes
1219f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  """
1220f384f0a06cf156c51c4ca584a4323e132c15f64fEino-Ville Talvala  return (e for e in entries if e.applied_visibility in visibilities)
12210b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12226c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkindef remove_synthetic(entries):
12236c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  """
12246c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  Filter the given entries by removing those that are synthetic.
12256c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin
12266c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  Args:
12276c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin    entries: An iterable of Entry nodes
12286c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin
12296c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  Yields:
12306c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin    An iterable of Entry nodes
12316c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  """
12326c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin  return (e for e in entries if not e.synthetic)
12336c936c18e02b122baaa3d5056b0555b6cff256f8Igor Murashkin
1234c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yehdef filter_ndk_visible(entries):
1235c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  """
1236c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  Filter the given entries by removing those that are not NDK visible.
1237c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh
1238c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  Args:
1239c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh    entries: An iterable of Entry nodes
1240c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh
1241c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  Yields:
1242c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh    An iterable of Entry nodes
1243c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  """
1244c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh  return (e for e in entries if e.applied_ndk_visible == 'true')
1245c6c2416a812ddb8bcb32fdefce1eff3a7ded9b61Yin-Chia Yeh
12460b080452cca90f215d10d636abfb47701d7518daIgor Murashkindef wbr(text):
12470b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  """
12480b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  Insert word break hints for the browser in the form of <wbr> HTML tags.
12490b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12500b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  Word breaks are inserted inside an HTML node only, so the nodes themselves
12510b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  will not be changed. Attributes are also left unchanged.
12520b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12530b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  The following rules apply to insert word breaks:
12540b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  - For characters in [ '.', '/', '_' ]
12550b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  - For uppercase letters inside a multi-word X.Y.Z (at least 3 parts)
12560b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12570b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  Args:
12580b080452cca90f215d10d636abfb47701d7518daIgor Murashkin    text: A string of text containing HTML content.
12590b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12600b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  Returns:
12610b080452cca90f215d10d636abfb47701d7518daIgor Murashkin    A string with <wbr> inserted by the above rules.
12620b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  """
12630b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  SPLIT_CHARS_LIST = ['.', '_', '/']
12640b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  SPLIT_CHARS = r'([.|/|_/,]+)' # split by these characters
12650b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  CAP_LETTER_MIN = 3 # at least 3 components split by above chars, i.e. x.y.z
12660b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  def wbr_filter(text):
12670b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      new_txt = text
12680b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12690b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # for johnyOrange.appleCider.redGuardian also insert wbr before the caps
12700b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # => johny<wbr>Orange.apple<wbr>Cider.red<wbr>Guardian
12710b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      for words in text.split(" "):
12720b080452cca90f215d10d636abfb47701d7518daIgor Murashkin        for char in SPLIT_CHARS_LIST:
12730b080452cca90f215d10d636abfb47701d7518daIgor Murashkin          # match at least x.y.z, don't match x or x.y
12740b080452cca90f215d10d636abfb47701d7518daIgor Murashkin          if len(words.split(char)) >= CAP_LETTER_MIN:
12750b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            new_word = re.sub(r"([a-z])([A-Z])", r"\1<wbr>\2", words)
12760b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            new_txt = new_txt.replace(words, new_word)
12770b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12780b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # e.g. X/Y/Z -> X/<wbr>Y/<wbr>/Z. also for X.Y.Z, X_Y_Z.
12790b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      new_txt = re.sub(SPLIT_CHARS, r"\1<wbr>", new_txt)
12800b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12810b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      return new_txt
12820b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12830b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  # Do not mangle HTML when doing the replace by using BeatifulSoup
12840b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  # - Use the 'html.parser' to avoid inserting <html><body> when decoding
12850b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  soup = bs4.BeautifulSoup(text, features='html.parser')
12860b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  wbr_tag = lambda: soup.new_tag('wbr') # must generate new tag every time
12870b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12880b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  for navigable_string in soup.findAll(text=True):
12890b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      parent = navigable_string.parent
12900b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12910b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # Insert each '$text<wbr>$foo' before the old '$text$foo'
12920b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      split_by_wbr_list = wbr_filter(navigable_string).split("<wbr>")
12930b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      for (split_string, last) in enumerate_with_last(split_by_wbr_list):
12940b080452cca90f215d10d636abfb47701d7518daIgor Murashkin          navigable_string.insert_before(split_string)
12950b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
12960b080452cca90f215d10d636abfb47701d7518daIgor Murashkin          if not last:
12970b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            # Note that 'insert' will move existing tags to this spot
12980b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            # so make a new tag instead
12990b080452cca90f215d10d636abfb47701d7518daIgor Murashkin            navigable_string.insert_before(wbr_tag())
13000b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
13010b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      # Remove the old unmodified text
13020b080452cca90f215d10d636abfb47701d7518daIgor Murashkin      navigable_string.extract()
13030b080452cca90f215d10d636abfb47701d7518daIgor Murashkin
13040b080452cca90f215d10d636abfb47701d7518daIgor Murashkin  return soup.decode()
1305