Home | History | Annotate | Download | only in docs
      1 #
      2 # Copyright (C) 2012 The Android Open Source Project
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #      http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 #
     16 
     17 """
     18 A set of helpers for rendering Mako templates with a Metadata model.
     19 """
     20 
     21 import metadata_model
     22 from collections import OrderedDict
     23 
     24 _context_buf = None
     25 
     26 def _is_sec_or_ins(x):
     27   return isinstance(x, metadata_model.Section) or    \
     28          isinstance(x, metadata_model.InnerNamespace)
     29 
     30 ##
     31 ## Metadata Helpers
     32 ##
     33 
     34 def find_all_sections(root):
     35   """
     36   Find all descendants that are Section or InnerNamespace instances.
     37 
     38   Args:
     39     root: a Metadata instance
     40 
     41   Returns:
     42     A list of Section/InnerNamespace instances
     43 
     44   Remarks:
     45     These are known as "sections" in the generated C code.
     46   """
     47   return root.find_all(_is_sec_or_ins)
     48 
     49 def find_parent_section(entry):
     50   """
     51   Find the closest ancestor that is either a Section or InnerNamespace.
     52 
     53   Args:
     54     entry: an Entry or Clone node
     55 
     56   Returns:
     57     An instance of Section or InnerNamespace
     58   """
     59   return entry.find_parent_first(_is_sec_or_ins)
     60 
     61 # find uniquely named entries (w/o recursing through inner namespaces)
     62 def find_unique_entries(node):
     63   """
     64   Find all uniquely named entries, without recursing through inner namespaces.
     65 
     66   Args:
     67     node: a Section or InnerNamespace instance
     68 
     69   Yields:
     70     A sequence of MergedEntry nodes representing an entry
     71 
     72   Remarks:
     73     This collapses multiple entries with the same fully qualified name into
     74     one entry (e.g. if there are multiple entries in different kinds).
     75   """
     76   if not isinstance(node, metadata_model.Section) and    \
     77      not isinstance(node, metadata_model.InnerNamespace):
     78       raise TypeError("expected node to be a Section or InnerNamespace")
     79 
     80   d = OrderedDict()
     81   # remove the 'kinds' from the path between sec and the closest entries
     82   # then search the immediate children of the search path
     83   search_path = isinstance(node, metadata_model.Section) and node.kinds \
     84                 or [node]
     85   for i in search_path:
     86       for entry in i.entries:
     87           d[entry.name] = entry
     88 
     89   for k,v in d.iteritems():
     90       yield v.merge()
     91 
     92 def path_name(node):
     93   """
     94   Calculate a period-separated string path from the root to this element,
     95   by joining the names of each node and excluding the Metadata/Kind nodes
     96   from the path.
     97 
     98   Args:
     99     node: a Node instance
    100 
    101   Returns:
    102     A string path
    103   """
    104 
    105   isa = lambda x,y: isinstance(x, y)
    106   fltr = lambda x: not isa(x, metadata_model.Metadata) and \
    107                    not isa(x, metadata_model.Kind)
    108 
    109   path = node.find_parents(fltr)
    110   path = list(path)
    111   path.reverse()
    112   path.append(node)
    113 
    114   return ".".join((i.name for i in path))
    115 
    116 ##
    117 ## Filters
    118 ##
    119 
    120 # abcDef.xyz -> ABC_DEF_XYZ
    121 def csym(name):
    122   """
    123   Convert an entry name string into an uppercase C symbol.
    124 
    125   Returns:
    126     A string
    127 
    128   Example:
    129     csym('abcDef.xyz') == 'ABC_DEF_XYZ'
    130   """
    131   newstr = name
    132   newstr = "".join([i.isupper() and ("_" + i) or i for i in newstr]).upper()
    133   newstr = newstr.replace(".", "_")
    134   return newstr
    135 
    136 # abcDef.xyz -> abc_def_xyz
    137 def csyml(name):
    138   """
    139   Convert an entry name string into a lowercase C symbol.
    140 
    141   Returns:
    142     A string
    143 
    144   Example:
    145     csyml('abcDef.xyz') == 'abc_def_xyz'
    146   """
    147   return csym(name).lower()
    148 
    149 # pad with spaces to make string len == size. add new line if too big
    150 def ljust(size, indent=4):
    151   """
    152   Creates a function that given a string will pad it with spaces to make
    153   the string length == size. Adds a new line if the string was too big.
    154 
    155   Args:
    156     size: an integer representing how much spacing should be added
    157     indent: an integer representing the initial indendation level
    158 
    159   Returns:
    160     A function that takes a string and returns a string.
    161 
    162   Example:
    163     ljust(8)("hello") == 'hello   '
    164 
    165   Remarks:
    166     Deprecated. Use pad instead since it works for non-first items in a
    167     Mako template.
    168   """
    169   def inner(what):
    170     newstr = what.ljust(size)
    171     if len(newstr) > size:
    172       return what + "\n" + "".ljust(indent + size)
    173     else:
    174       return newstr
    175   return inner
    176 
    177 def _find_new_line():
    178 
    179   if _context_buf is None:
    180     raise ValueError("Context buffer was not set")
    181 
    182   buf = _context_buf
    183   x = -1 # since the first read is always ''
    184   cur_pos = buf.tell()
    185   while buf.tell() > 0 and buf.read(1) != '\n':
    186     buf.seek(cur_pos - x)
    187     x = x + 1
    188 
    189   buf.seek(cur_pos)
    190 
    191   return int(x)
    192 
    193 # Pad the string until the buffer reaches the desired column.
    194 # If string is too long, insert a new line with 'col' spaces instead
    195 def pad(col):
    196   """
    197   Create a function that given a string will pad it to the specified column col.
    198   If the string overflows the column, put the string on a new line and pad it.
    199 
    200   Args:
    201     col: an integer specifying the column number
    202 
    203   Returns:
    204     A function that given a string will produce a padded string.
    205 
    206   Example:
    207     pad(8)("hello") == 'hello   '
    208 
    209   Remarks:
    210     This keeps track of the line written by Mako so far, so it will always
    211     align to the column number correctly.
    212   """
    213   def inner(what):
    214     wut = int(col)
    215     current_col = _find_new_line()
    216 
    217     if len(what) > wut - current_col:
    218       return what + "\n".ljust(col)
    219     else:
    220       return what.ljust(wut - current_col)
    221   return inner
    222 
    223 # int32 -> TYPE_INT32, byte -> TYPE_BYTE, etc. note that enum -> TYPE_INT32
    224 def ctype_enum(what):
    225   """
    226   Generate a camera_metadata_type_t symbol from a type string.
    227 
    228   Args:
    229     what: a type string
    230 
    231   Returns:
    232     A string representing the camera_metadata_type_t
    233 
    234   Example:
    235     ctype_enum('int32') == 'TYPE_INT32'
    236     ctype_enum('int64') == 'TYPE_INT64'
    237     ctype_enum('float') == 'TYPE_FLOAT'
    238 
    239   Remarks:
    240     An enum is coerced to a byte since the rest of the camera_metadata
    241     code doesn't support enums directly yet.
    242   """
    243   return 'TYPE_%s' %(what.upper())
    244