This is a comment for Javadoc\n" + " * with multiple lines, that should be\n" + " * formatted better
\n" + " *That covers multiple lines as well
\n" + " * and references {@link CaptureRequest#CONTROL_MODE android.control.mode}\n" + " *\n" + " * @see CaptureRequest#CONTROL_MODE\n" """ def javadoc_formatter(text): comment_prefix = " " * indent + " * "; # render with markdown => HTML javatext = md(text, JAVADOC_IMAGE_SRC_METADATA) # Identity transform for javadoc links def javadoc_link_filter(target, shortname): return '{@link %s %s}' % (target, shortname) javatext = filter_links(javatext, javadoc_link_filter) # Crossref tag names kind_mapping = { 'static': 'CameraCharacteristics', 'dynamic': 'CaptureResult', 'controls': 'CaptureRequest' } # Convert metadata entry "android.x.y.z" to form # "{@link CaptureRequest#X_Y_Z android.x.y.z}" def javadoc_crossref_filter(node): if node.applied_visibility in ('public', 'java_public'): return '{@link %s#%s %s}' % (kind_mapping[node.kind], jkey_identifier(node.name), node.name) else: return node.name # For each public tag "android.x.y.z" referenced, add a # "@see CaptureRequest#X_Y_Z" def javadoc_crossref_see_filter(node_set): node_set = (x for x in node_set if x.applied_visibility in ('public', 'java_public')) text = '\n' for node in node_set: text = text + '\n@see %s#%s' % (kind_mapping[node.kind], jkey_identifier(node.name)) return text if text != '\n' else '' javatext = filter_tags(javatext, metadata, javadoc_crossref_filter, javadoc_crossref_see_filter) def line_filter(line): # Indent each line # Add ' * ' to it for stylistic reasons # Strip right side of trailing whitespace return (comment_prefix + line).rstrip() # Process each line with above filter javatext = "\n".join(line_filter(i) for i in javatext.split("\n")) + "\n" return javatext return javadoc_formatter def ndkdoc(metadata, indent = 4): """ Returns a function to format a markdown syntax text block as a NDK camera API C/C++ comment section, given a set of metadata Args: metadata: A Metadata instance, representing the the top-level root of the metadata for cross-referencing indent: baseline level of indentation for comment block Returns: A function that transforms a String text block as follows: - Indent and * for insertion into a comment block - Trailing whitespace removed - Entire body rendered via markdown - All tag names converted to appropriate NDK tag name for each tag Example: "This is a comment for NDK\n" + " with multiple lines, that should be \n" + " formatted better\n" + "\n" + " That covers multiple lines as well\n" " And references android.control.mode\n" transforms to " * This is a comment for NDK\n" + " * with multiple lines, that should be\n" + " * formatted better\n" + " * That covers multiple lines as well\n" + " * and references ACAMERA_CONTROL_MODE\n" + " *\n" + " * @see ACAMERA_CONTROL_MODE\n" """ def ndkdoc_formatter(text): # render with markdown => HTML ndktext = md(text, NDKDOC_IMAGE_SRC_METADATA, False) # Convert metadata entry "android.x.y.z" to form # NDK tag format of "ACAMERA_X_Y_Z" def ndkdoc_crossref_filter(node): if node.applied_ndk_visible == 'true': return csym(ndk(node.name)) else: return node.name # For each public tag "android.x.y.z" referenced, add a # "@see ACAMERA_X_Y_Z" def ndkdoc_crossref_see_filter(node_set): node_set = (x for x in node_set if x.applied_ndk_visible == 'true') text = '\n' for node in node_set: text = text + '\n@see %s' % (csym(ndk(node.name))) return text if text != '\n' else '' ndktext = filter_tags(ndktext, metadata, ndkdoc_crossref_filter, ndkdoc_crossref_see_filter) ndktext = ndk_replace_tag_wildcards(ndktext, metadata) comment_prefix = " " * indent + " * "; def line_filter(line): # Indent each line # Add ' * ' to it for stylistic reasons # Strip right side of trailing whitespace return (comment_prefix + line).rstrip() # Process each line with above filter ndktext = "\n".join(line_filter(i) for i in ndktext.split("\n")) + "\n" return ndktext return ndkdoc_formatter def dedent(text): """ Remove all common indentation from every line but the 0th. This will avoid getting blocks when rendering text via markdown.
Ignoring the 0th line will also allow the 0th line not to be aligned.
Args:
text: A string of text to dedent.
Returns:
String dedented by above rules.
For example:
assertEquals("bar\nline1\nline2", dedent("bar\n line1\n line2"))
assertEquals("bar\nline1\nline2", dedent(" bar\n line1\n line2"))
assertEquals("bar\n line1\nline2", dedent(" bar\n line1\n line2"))
"""
text = textwrap.dedent(text)
text_lines = text.split('\n')
text_not_first = "\n".join(text_lines[1:])
text_not_first = textwrap.dedent(text_not_first)
text = text_lines[0] + "\n" + text_not_first
return text
def md(text, img_src_prefix="", table_ext=True):
"""
Run text through markdown to produce HTML.
This also removes all common indentation from every line but the 0th.
This will avoid getting blocks in markdown.
Ignoring the 0th line will also allow the 0th line not to be aligned.
Args:
text: A markdown-syntax using block of text to format.
img_src_prefix: An optional string to prepend to each
Returns:
String rendered by markdown and other rules applied (see above).
For example, this avoids the following situation:
foo
bar
bar
foo
bar
bar
Instead we get the more natural expected result:
foo
bar
bar
"""
text = dedent(text)
# full list of extensions at http://pythonhosted.org/Markdown/extensions/
md_extensions = ['tables'] if table_ext else []# make with ASCII |_| tables
# render with markdown
text = markdown.markdown(text, md_extensions)
# prepend a prefix to each ->
text = re.sub(r'src="([^"]*)"', 'src="' + img_src_prefix + r'\1"', text)
return text
def filter_tags(text, metadata, filter_function, summary_function = None):
"""
Find all references to tags in the form outer_namespace.xxx.yyy[.zzz] in
the provided text, and pass them through filter_function and summary_function.
Used to linkify entry names in HMTL, javadoc output.
Args:
text: A string representing a block of text destined for output
metadata: A Metadata instance, the root of the metadata properties tree
filter_function: A Node->string function to apply to each node
when found in text; the string returned replaces the tag name in text.
summary_function: A Node list->string function that is provided the list of
unique tag nodes found in text, and which must return a string that is
then appended to the end of the text. The list is sorted alphabetically
by node name.
"""
tag_set = set()
def name_match(name):
return lambda node: node.name == name
# Match outer_namespace.x.y or outer_namespace.x.y.z, making sure
# to grab .z and not just outer_namespace.x.y. (sloppy, but since we
# check for validity, a few false positives don't hurt).
# Try to ignore items of the form {@link ...
for outer_namespace in metadata.outer_namespaces:
tag_match = r"(?> sys.stderr,\
" WARNING: Could not crossref likely reference {%s}" % (match.group(0))
return whole_match
text = re.sub(tag_match, filter_sub, text)
if summary_function is not None:
return text + summary_function(sorted(tag_set, key=lambda x: x.name))
else:
return text
def ndk_replace_tag_wildcards(text, metadata):
"""
Find all references to tags in the form android.xxx.* or android.xxx.yyy.*
in the provided text, and replace them by NDK format of "ACAMERA_XXX_*" or
"ACAMERA_XXX_YYY_*"
Args:
text: A string representing a block of text destined for output
metadata: A Metadata instance, the root of the metadata properties tree
"""
tag_match = r"android\.([a-zA-Z0-9\n]+)\.\*"
tag_match_2 = r"android\.([a-zA-Z0-9\n]+)\.([a-zA-Z0-9\n]+)\*"
def filter_sub(match):
return "ACAMERA_" + match.group(1).upper() + "_*"
def filter_sub_2(match):
return "ACAMERA_" + match.group(1).upper() + match.group(2).upper() + "_*"
text = re.sub(tag_match, filter_sub, text)
text = re.sub(tag_match_2, filter_sub_2, text)
return text
def filter_links(text, filter_function, summary_function = None):
"""
Find all references to tags in the form {@link xxx#yyy [zzz]} in the
provided text, and pass them through filter_function and
summary_function.
Used to linkify documentation cross-references in HMTL, javadoc output.
Args:
text: A string representing a block of text destined for output
metadata: A Metadata instance, the root of the metadata properties tree
filter_function: A (string, string)->string function to apply to each 'xxx#yyy',
zzz pair when found in text; the string returned replaces the tag name in text.
summary_function: A string list->string function that is provided the list of
unique targets found in text, and which must return a string that is
then appended to the end of the text. The list is sorted alphabetically
by node name.
"""
target_set = set()
def name_match(name):
return lambda node: node.name == name
tag_match = r"\{@link\s+([^\s\}]+)([^\}]*)\}"
def filter_sub(match):
whole_match = match.group(0)
target = match.group(1)
shortname = match.group(2).strip()
#print "Found link '%s' as '%s' -> '%s'" % (target, shortname, filter_function(target, shortname))
# Replace match with crossref
target_set.add(target)
return filter_function(target, shortname)
text = re.sub(tag_match, filter_sub, text)
if summary_function is not None:
return text + summary_function(sorted(target_set))
else:
return text
def any_visible(section, kind_name, visibilities):
"""
Determine if entries in this section have an applied visibility that's in
the list of given visibilities.
Args:
section: A section of metadata
kind_name: A name of the kind, i.e. 'dynamic' or 'static' or 'controls'
visibilities: An iterable of visibilities to match against
Returns:
True if the section has any entries with any of the given visibilities. False otherwise.
"""
for inner_namespace in get_children_by_filtering_kind(section, kind_name,
'namespaces'):
if any(filter_visibility(inner_namespace.merged_entries, visibilities)):
return True
return any(filter_visibility(get_children_by_filtering_kind(section, kind_name,
'merged_entries'),
visibilities))
def filter_visibility(entries, visibilities):
"""
Remove entries whose applied visibility is not in the supplied visibilities.
Args:
entries: An iterable of Entry nodes
visibilities: An iterable of visibilities to filter against
Yields:
An iterable of Entry nodes
"""
return (e for e in entries if e.applied_visibility in visibilities)
def remove_synthetic(entries):
"""
Filter the given entries by removing those that are synthetic.
Args:
entries: An iterable of Entry nodes
Yields:
An iterable of Entry nodes
"""
return (e for e in entries if not e.synthetic)
def filter_ndk_visible(entries):
"""
Filter the given entries by removing those that are not NDK visible.
Args:
entries: An iterable of Entry nodes
Yields:
An iterable of Entry nodes
"""
return (e for e in entries if e.applied_ndk_visible == 'true')
def wbr(text):
"""
Insert word break hints for the browser in the form of HTML tags.
Word breaks are inserted inside an HTML node only, so the nodes themselves
will not be changed. Attributes are also left unchanged.
The following rules apply to insert word breaks:
- For characters in [ '.', '/', '_' ]
- For uppercase letters inside a multi-word X.Y.Z (at least 3 parts)
Args:
text: A string of text containing HTML content.
Returns:
A string with inserted by the above rules.
"""
SPLIT_CHARS_LIST = ['.', '_', '/']
SPLIT_CHARS = r'([.|/|_/,]+)' # split by these characters
CAP_LETTER_MIN = 3 # at least 3 components split by above chars, i.e. x.y.z
def wbr_filter(text):
new_txt = text
# for johnyOrange.appleCider.redGuardian also insert wbr before the caps
# => johnyOrange.appleCider.redGuardian
for words in text.split(" "):
for char in SPLIT_CHARS_LIST:
# match at least x.y.z, don't match x or x.y
if len(words.split(char)) >= CAP_LETTER_MIN:
new_word = re.sub(r"([a-z])([A-Z])", r"\1\2", words)
new_txt = new_txt.replace(words, new_word)
# e.g. X/Y/Z -> X/Y//Z. also for X.Y.Z, X_Y_Z.
new_txt = re.sub(SPLIT_CHARS, r"\1", new_txt)
return new_txt
# Do not mangle HTML when doing the replace by using BeatifulSoup
# - Use the 'html.parser' to avoid inserting when decoding
soup = bs4.BeautifulSoup(text, features='html.parser')
wbr_tag = lambda: soup.new_tag('wbr') # must generate new tag every time
for navigable_string in soup.findAll(text=True):
parent = navigable_string.parent
# Insert each '$text$foo' before the old '$text$foo'
split_by_wbr_list = wbr_filter(navigable_string).split("")
for (split_string, last) in enumerate_with_last(split_by_wbr_list):
navigable_string.insert_before(split_string)
if not last:
# Note that 'insert' will move existing tags to this spot
# so make a new tag instead
navigable_string.insert_before(wbr_tag())
# Remove the old unmodified text
navigable_string.extract()
return soup.decode()