19ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian#!/usr/bin/env python
29ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian#
39ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian# Copyright (C) 2017 The Android Open Source Project
49ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian#
59ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian# Licensed under the Apache License, Version 2.0 (the "License");
69ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian# you may not use this file except in compliance with the License.
79ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian# You may obtain a copy of the License at
89ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian#
99ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian#      http://www.apache.org/licenses/LICENSE-2.0
109ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian#
119ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian# Unless required by applicable law or agreed to in writing, software
129ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian# distributed under the License is distributed on an "AS IS" BASIS,
139ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
149ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian# See the License for the specific language governing permissions and
159ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian# limitations under the License.
169ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian
179ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian"""Outputs HTML based on an input JSON file.
189ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian
199ae0b401654f2faf408bb37808e7f6a5ede976b6Adam VartanianOutputs HTML tables suitable for inclusion in the Android documentation that
209ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanianreflect the crypto algorithm support shown in the provided data file.
219ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian"""
229ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian
23748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanianimport argparse
24befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanianimport operator
259ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian
26befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanianimport crypto_docs
27befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian
289ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian
2922051136d1344e34c05659420bc49372040bd75eAdam Vartanianfind_by_name = crypto_docs.find_by_name
3022051136d1344e34c05659420bc49372040bd75eAdam Vartanian
3122051136d1344e34c05659420bc49372040bd75eAdam Vartanian
329ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartaniandef sort_by_name(seq):
339ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian    return sorted(seq, key=lambda x: x['name'])
349ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian
359ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian
361179672f16736ef3437dd4603132c5f25809179dAdam Vartaniandef has_notes(category):
371179672f16736ef3437dd4603132c5f25809179dAdam Vartanian    for algorithm in category['algorithms']:
381179672f16736ef3437dd4603132c5f25809179dAdam Vartanian        if 'note' in algorithm:
391179672f16736ef3437dd4603132c5f25809179dAdam Vartanian            return True
401179672f16736ef3437dd4603132c5f25809179dAdam Vartanian    return False
411179672f16736ef3437dd4603132c5f25809179dAdam Vartanian
421179672f16736ef3437dd4603132c5f25809179dAdam Vartanian
431179672f16736ef3437dd4603132c5f25809179dAdam Vartanian# Prevents the given value from being word-wrapped.  This is mainly to ensure that
441179672f16736ef3437dd4603132c5f25809179dAdam Vartanian# long identifiers with hyphens, like OAEPwithSHA-1andMGF1Padding, don't get word-wrapped
451179672f16736ef3437dd4603132c5f25809179dAdam Vartanian# at the hyphen.
461179672f16736ef3437dd4603132c5f25809179dAdam Vartaniandef nowrap(value):
471179672f16736ef3437dd4603132c5f25809179dAdam Vartanian    return '<span style="white-space: nowrap">%s</span>' % value
481179672f16736ef3437dd4603132c5f25809179dAdam Vartanian
491179672f16736ef3437dd4603132c5f25809179dAdam Vartanian
509ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartaniandef main():
51748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    parser = argparse.ArgumentParser(description='Output algorithm support HTML tables')
52748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    parser.add_argument('--for_javadoc',
53748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                        action='store_true',
54748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                        help='If specified, format for inclusion in class documentation')
551179672f16736ef3437dd4603132c5f25809179dAdam Vartanian    parser.add_argument('--category',
561179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                        action='append',
571179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                        help='The category to display, may be specified multiple times')
58748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    parser.add_argument('file',
59748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                        help='The JSON file to use for data')
60748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    args = parser.parse_args()
61748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian
62748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    output = []
63748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    data = crypto_docs.load_json(args.file)
649ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian    categories = sort_by_name(data['categories'])
65748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    output.append('<h2 id="SupportedAlgorithms">Supported Algorithms</h2>')
66748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    output.append('')
67748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    output.append('<ul>')
689ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian    for category in categories:
6922051136d1344e34c05659420bc49372040bd75eAdam Vartanian        if not category['name'].endswith('.Enabled'):
7022051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('  <li><a href="#Supported{name}">'
7122051136d1344e34c05659420bc49372040bd75eAdam Vartanian                   '<code>{name}</code></a></li>'.format(**category))
72748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    output.append('</ul>')
739ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian    for category in categories:
741179672f16736ef3437dd4603132c5f25809179dAdam Vartanian        if args.category and category['name'] not in args.category:
751179672f16736ef3437dd4603132c5f25809179dAdam Vartanian            continue
761179672f16736ef3437dd4603132c5f25809179dAdam Vartanian        show_notes = has_notes(category)
7722051136d1344e34c05659420bc49372040bd75eAdam Vartanian        if category['name'].endswith('.Enabled'):
7822051136d1344e34c05659420bc49372040bd75eAdam Vartanian            # These are handled in the "Supported" section below
7922051136d1344e34c05659420bc49372040bd75eAdam Vartanian            continue
80befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian        if category['name'] == 'Cipher':
81befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # We display ciphers in a four-column table to conserve space and
82befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # so that it's more comprehensible.  To do this, we have to
83befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # collapse all our ciphers into "equivalence classes" of a sort.
84befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian
85befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # First, collect the relevant data for each algorithm into a tuple.
86befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # The mode and padding are in lists because we are going to collapse
87befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # multiple tuples with those in later steps.
88befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            algorithms = sort_by_name(category['algorithms'])
89befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            tuples = []
90befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            for algorithm in algorithms:
91befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                name, mode, padding = algorithm['name'].split('/')
92befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                tuples.append((
93befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    name,
94befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    [mode],
95befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    [padding],
96befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    algorithm['supported_api_levels'],
971179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    'deprecated' in algorithm and algorithm['deprecated'],
981179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    algorithm.get('note', '')))
99befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # Sort the tuples by all items except padding, then collapse
100befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # items with all non-padding values the same (which will always be
101befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # neighboring items) into a single item.
102befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            tuples.sort(key=operator.itemgetter(0, 1, 3, 4))
103befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            i = 0
104befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            while i < len(tuples) - 1:
105befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                if (tuples[i][0] == tuples[i+1][0]
106befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    and tuples[i][1] == tuples[i+1][1]
107befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    and tuples[i][3] == tuples[i+1][3]
1081179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    and tuples[i][4] == tuples[i+1][4]
1091179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    and tuples[i][5] == tuples[i+1][5]):
110befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    tuples[i][2].extend(tuples[i+1][2])
111befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    del tuples[i+1]
112befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                else:
113befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    i += 1
114befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # Do the same thing as above, but with modes.
115befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            tuples.sort(key=operator.itemgetter(0, 2, 3, 4))
116befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            i = 0
117befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            while i < len(tuples) - 1:
118befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                if (tuples[i][0] == tuples[i+1][0]
119befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    and tuples[i][2] == tuples[i+1][2]
120befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    and tuples[i][3] == tuples[i+1][3]
1211179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    and tuples[i][4] == tuples[i+1][4]
1221179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    and tuples[i][5] == tuples[i+1][5]):
123befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    tuples[i][1].extend(tuples[i+1][1])
124befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    del tuples[i+1]
125befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                else:
126befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    i += 1
127befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # Display the table with rowspans for those entries where all the
128befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            # items have the same algorithm, mode, etc
129748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('<h3 id="Supported{name}">{name}</h3>'.format(**category))
130748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('<table>')
131748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('  <thead>')
132748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('    <tr>')
133748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('      <th>Algorithm</th>')
134748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('      <th>Modes</th>')
135748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('      <th>Paddings</th>')
136748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('      <th>Supported API Levels</th>')
1371179672f16736ef3437dd4603132c5f25809179dAdam Vartanian            if show_notes:
1381179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                output.append('      <th>Notes</th>')
139748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('    </tr>')
140748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('  </thead>')
141748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('  <tbody>')
142befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            tuples.sort(key=operator.itemgetter(0, 4, 1, 2, 3))
143befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            i = 0
144befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            cur_deprecated = None
145befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            cur_algorithm = None
146befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            cur_mode = None
147befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            while i < len(tuples):
148befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                row = tuples[i]
149befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                if row[4] != cur_deprecated:
150befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    cur_deprecated = row[4]
1511179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    cur_note = row[5]
152befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    cur_algorithm = None
153befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    cur_mode = None
154befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                if cur_deprecated:
155748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                    output.append('    <tr class="deprecated">')
156befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                else:
157748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                    output.append('    <tr>')
158befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                if row[0] != cur_algorithm:
159befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    cur_algorithm = row[0]
160befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    cur_mode = None
161befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    j = i + 1
162befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    while (j < len(tuples)
163befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                           and tuples[j][4] == cur_deprecated
1641179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                           and tuples[j][5] == cur_note
165befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                           and tuples[j][0] == cur_algorithm):
166befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                        j += 1
167befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    rowspan = j - i
168befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    if rowspan > 1:
1691179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                        output.append('      <td rowspan="%d">%s</td>' % (rowspan, nowrap(cur_algorithm)))
170befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    else:
1711179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                        output.append('      <td>%s</td>' % nowrap(cur_algorithm))
172befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                if row[1] != cur_mode:
173befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    cur_mode = row[1]
174befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    j = i + 1
175befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    while (j < len(tuples)
176befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                           and tuples[j][4] == cur_deprecated
1771179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                           and tuples[j][5] == cur_note
178befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                           and tuples[j][0] == cur_algorithm
179befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                           and tuples[j][1] == cur_mode):
180befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                        j += 1
181befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    rowspan = j - i
1821179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    modestring = '<br>'.join([nowrap(x) for x in cur_mode])
183befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    if rowspan > 1:
184748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                        output.append('      <td rowspan="%d">%s</td>' % (rowspan, modestring))
185befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                    else:
186748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                        output.append('      <td>%s</td>' % modestring)
1871179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                output.append('      <td>%s</td>' % '<br>'.join([nowrap(x) for x in row[2]]))
1881179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                output.append('      <td>%s</td>' % nowrap(row[3]))
1891179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                if show_notes:
1901179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    output.append('      <td>%s</td>' % row[5])
191748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                output.append('    </tr>')
192befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                i += 1
193748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('  </tbody>')
194748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('</table>')
19522051136d1344e34c05659420bc49372040bd75eAdam Vartanian        elif category['name'].endswith('.Supported'):
19622051136d1344e34c05659420bc49372040bd75eAdam Vartanian            # Some categories come with a "Supported" and "Enabled" list, and we
19722051136d1344e34c05659420bc49372040bd75eAdam Vartanian            # group those together in one table for display.  Every entry that's enabled
19822051136d1344e34c05659420bc49372040bd75eAdam Vartanian            # must be supported, so we can just look up the enabled version for each
19922051136d1344e34c05659420bc49372040bd75eAdam Vartanian            # supported item
20022051136d1344e34c05659420bc49372040bd75eAdam Vartanian            basename = category['name'][:-len('.Supported')]
20122051136d1344e34c05659420bc49372040bd75eAdam Vartanian            supported = sort_by_name(category['algorithms'])
20222051136d1344e34c05659420bc49372040bd75eAdam Vartanian            enabled = sort_by_name(find_by_name(categories, basename + '.Enabled')['algorithms'])
20322051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('<h3 id="Supported{0}">{0}</h3>'.format(basename))
20422051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('<table>')
20522051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('  <thead>')
20622051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('    <tr>')
20722051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('      <th>Algorithm</th>')
20822051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('      <th>Supported API Levels</th>')
20922051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('      <th>Enabled By Default</th>')
2101179672f16736ef3437dd4603132c5f25809179dAdam Vartanian            if show_notes:
2111179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                output.append('      <th>Notes</th>')
21222051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('    </tr>')
21322051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('  </thead>')
21422051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('  <tbody>')
21522051136d1344e34c05659420bc49372040bd75eAdam Vartanian            for algorithm in supported:
21622051136d1344e34c05659420bc49372040bd75eAdam Vartanian                if 'deprecated' in algorithm and algorithm['deprecated']:
21722051136d1344e34c05659420bc49372040bd75eAdam Vartanian                    output.append('    <tr class="deprecated">')
21822051136d1344e34c05659420bc49372040bd75eAdam Vartanian                else:
21922051136d1344e34c05659420bc49372040bd75eAdam Vartanian                    output.append('    <tr>')
2201179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                output.append('      <td>%s</td>' % nowrap(algorithm['name']))
2211179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                output.append('      <td>%s</td>' % nowrap(algorithm['supported_api_levels']))
22222051136d1344e34c05659420bc49372040bd75eAdam Vartanian                enabled_alg = find_by_name(enabled, algorithm['name'])
22322051136d1344e34c05659420bc49372040bd75eAdam Vartanian                if enabled_alg is None:
22422051136d1344e34c05659420bc49372040bd75eAdam Vartanian                    output.append('      <td></td>')
22522051136d1344e34c05659420bc49372040bd75eAdam Vartanian                else:
2261179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    output.append('      <td>%s</td>' % nowrap(enabled_alg['supported_api_levels']))
2271179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                if show_notes:
2281179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    if 'note' in algorithm:
2291179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                        output.append('      <td>%s</td>' % algorithm['note'])
2301179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    else:
2311179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                        output.append('      <td></td>')
23222051136d1344e34c05659420bc49372040bd75eAdam Vartanian                output.append('    </tr>')
23322051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('  </tbody>')
23422051136d1344e34c05659420bc49372040bd75eAdam Vartanian            output.append('</table>')
235befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian        else:
236748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('<h3 id="Supported{name}">{name}</h3>'.format(**category))
237748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('<table>')
238748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('  <thead>')
239748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('    <tr>')
240748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('      <th>Algorithm</th>')
241748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('      <th>Supported API Levels</th>')
2421179672f16736ef3437dd4603132c5f25809179dAdam Vartanian            if show_notes:
2431179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                output.append('      <th>Notes</th>')
244748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('    </tr>')
245748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('  </thead>')
246748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('  <tbody>')
247befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            algorithms = sort_by_name(category['algorithms'])
248befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian            for algorithm in algorithms:
249befc86a711b876ed6523fbe453dfa7a9adb345afAdam Vartanian                if 'deprecated' in algorithm and algorithm['deprecated']:
250748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                    output.append('    <tr class="deprecated">')
251748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                else:
252748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                    output.append('    <tr>')
2531179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                output.append('      <td>%s</td>' % nowrap(algorithm['name']))
2541179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                output.append('      <td>%s</td>' % nowrap(algorithm['supported_api_levels']))
2551179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                if show_notes:
2561179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    if 'note' in algorithm:
2571179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                        output.append('      <td>%s</td>' % algorithm['note'])
2581179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                    else:
2591179672f16736ef3437dd4603132c5f25809179dAdam Vartanian                        output.append('      <td></td>')
260748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian                output.append('    </tr>')
261748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('  </tbody>')
262748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output.append('</table>')
263748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    if args.for_javadoc:
264748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian        for i in range(len(output)):
265748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian            output[i] = ' * ' + output[i]
266748190c9f4fb5171888cb0287adfb5b64f41607bAdam Vartanian    print '\n'.join(output)
2679ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian
2689ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian
2699ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanianif __name__ == '__main__':
2709ae0b401654f2faf408bb37808e7f6a5ede976b6Adam Vartanian    main()
271