blob: 1fc96065d42850b71b171d7e1357df85b54e12c6 [file] [log] [blame]
Adam Vartanian9ae0b402017-03-08 16:39:31 +00001#!/usr/bin/env python
2#
3# Copyright (C) 2017 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Outputs HTML based on an input JSON file.
18
19Outputs HTML tables suitable for inclusion in the Android documentation that
20reflect the crypto algorithm support shown in the provided data file.
21"""
22
Adam Vartanian748190c2017-03-20 11:16:44 +000023import argparse
Adam Vartanianbefc86a2017-03-16 14:26:08 +000024import operator
Adam Vartanian9ae0b402017-03-08 16:39:31 +000025
Adam Vartanianbefc86a2017-03-16 14:26:08 +000026import crypto_docs
27
Adam Vartanian9ae0b402017-03-08 16:39:31 +000028
Adam Vartanian22051132017-05-04 12:11:30 +010029find_by_name = crypto_docs.find_by_name
30
31
Adam Vartanian9ae0b402017-03-08 16:39:31 +000032def sort_by_name(seq):
33 return sorted(seq, key=lambda x: x['name'])
34
35
Adam Vartanian11796722018-02-02 14:09:14 +000036def has_notes(category):
37 for algorithm in category['algorithms']:
38 if 'note' in algorithm:
39 return True
40 return False
41
42
43# Prevents the given value from being word-wrapped. This is mainly to ensure that
44# long identifiers with hyphens, like OAEPwithSHA-1andMGF1Padding, don't get word-wrapped
45# at the hyphen.
46def nowrap(value):
47 return '<span style="white-space: nowrap">%s</span>' % value
48
49
Adam Vartanian9ae0b402017-03-08 16:39:31 +000050def main():
Adam Vartanian748190c2017-03-20 11:16:44 +000051 parser = argparse.ArgumentParser(description='Output algorithm support HTML tables')
52 parser.add_argument('--for_javadoc',
53 action='store_true',
54 help='If specified, format for inclusion in class documentation')
Adam Vartanian11796722018-02-02 14:09:14 +000055 parser.add_argument('--category',
56 action='append',
57 help='The category to display, may be specified multiple times')
Adam Vartanian748190c2017-03-20 11:16:44 +000058 parser.add_argument('file',
59 help='The JSON file to use for data')
60 args = parser.parse_args()
61
62 output = []
63 data = crypto_docs.load_json(args.file)
Adam Vartanian9ae0b402017-03-08 16:39:31 +000064 categories = sort_by_name(data['categories'])
Adam Vartanian748190c2017-03-20 11:16:44 +000065 output.append('<h2 id="SupportedAlgorithms">Supported Algorithms</h2>')
66 output.append('')
67 output.append('<ul>')
Adam Vartanian9ae0b402017-03-08 16:39:31 +000068 for category in categories:
Adam Vartanian22051132017-05-04 12:11:30 +010069 if not category['name'].endswith('.Enabled'):
70 output.append(' <li><a href="#Supported{name}">'
71 '<code>{name}</code></a></li>'.format(**category))
Adam Vartanian748190c2017-03-20 11:16:44 +000072 output.append('</ul>')
Adam Vartanian9ae0b402017-03-08 16:39:31 +000073 for category in categories:
Adam Vartanian11796722018-02-02 14:09:14 +000074 if args.category and category['name'] not in args.category:
75 continue
76 show_notes = has_notes(category)
Adam Vartanian22051132017-05-04 12:11:30 +010077 if category['name'].endswith('.Enabled'):
78 # These are handled in the "Supported" section below
79 continue
Adam Vartanianbefc86a2017-03-16 14:26:08 +000080 if category['name'] == 'Cipher':
81 # We display ciphers in a four-column table to conserve space and
82 # so that it's more comprehensible. To do this, we have to
83 # collapse all our ciphers into "equivalence classes" of a sort.
84
85 # First, collect the relevant data for each algorithm into a tuple.
86 # The mode and padding are in lists because we are going to collapse
87 # multiple tuples with those in later steps.
88 algorithms = sort_by_name(category['algorithms'])
89 tuples = []
90 for algorithm in algorithms:
91 name, mode, padding = algorithm['name'].split('/')
92 tuples.append((
93 name,
94 [mode],
95 [padding],
96 algorithm['supported_api_levels'],
Adam Vartanian11796722018-02-02 14:09:14 +000097 'deprecated' in algorithm and algorithm['deprecated'],
98 algorithm.get('note', '')))
Adam Vartanianbefc86a2017-03-16 14:26:08 +000099 # Sort the tuples by all items except padding, then collapse
100 # items with all non-padding values the same (which will always be
101 # neighboring items) into a single item.
102 tuples.sort(key=operator.itemgetter(0, 1, 3, 4))
103 i = 0
104 while i < len(tuples) - 1:
105 if (tuples[i][0] == tuples[i+1][0]
106 and tuples[i][1] == tuples[i+1][1]
107 and tuples[i][3] == tuples[i+1][3]
Adam Vartanian11796722018-02-02 14:09:14 +0000108 and tuples[i][4] == tuples[i+1][4]
109 and tuples[i][5] == tuples[i+1][5]):
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000110 tuples[i][2].extend(tuples[i+1][2])
111 del tuples[i+1]
112 else:
113 i += 1
114 # Do the same thing as above, but with modes.
115 tuples.sort(key=operator.itemgetter(0, 2, 3, 4))
116 i = 0
117 while i < len(tuples) - 1:
118 if (tuples[i][0] == tuples[i+1][0]
119 and tuples[i][2] == tuples[i+1][2]
120 and tuples[i][3] == tuples[i+1][3]
Adam Vartanian11796722018-02-02 14:09:14 +0000121 and tuples[i][4] == tuples[i+1][4]
122 and tuples[i][5] == tuples[i+1][5]):
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000123 tuples[i][1].extend(tuples[i+1][1])
124 del tuples[i+1]
125 else:
126 i += 1
127 # Display the table with rowspans for those entries where all the
128 # items have the same algorithm, mode, etc
Adam Vartanian748190c2017-03-20 11:16:44 +0000129 output.append('<h3 id="Supported{name}">{name}</h3>'.format(**category))
130 output.append('<table>')
131 output.append(' <thead>')
132 output.append(' <tr>')
133 output.append(' <th>Algorithm</th>')
134 output.append(' <th>Modes</th>')
135 output.append(' <th>Paddings</th>')
136 output.append(' <th>Supported API Levels</th>')
Adam Vartanian11796722018-02-02 14:09:14 +0000137 if show_notes:
138 output.append(' <th>Notes</th>')
Adam Vartanian748190c2017-03-20 11:16:44 +0000139 output.append(' </tr>')
140 output.append(' </thead>')
141 output.append(' <tbody>')
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000142 tuples.sort(key=operator.itemgetter(0, 4, 1, 2, 3))
143 i = 0
144 cur_deprecated = None
145 cur_algorithm = None
146 cur_mode = None
147 while i < len(tuples):
148 row = tuples[i]
149 if row[4] != cur_deprecated:
150 cur_deprecated = row[4]
Adam Vartanian11796722018-02-02 14:09:14 +0000151 cur_note = row[5]
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000152 cur_algorithm = None
153 cur_mode = None
154 if cur_deprecated:
Adam Vartanian748190c2017-03-20 11:16:44 +0000155 output.append(' <tr class="deprecated">')
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000156 else:
Adam Vartanian748190c2017-03-20 11:16:44 +0000157 output.append(' <tr>')
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000158 if row[0] != cur_algorithm:
159 cur_algorithm = row[0]
160 cur_mode = None
161 j = i + 1
162 while (j < len(tuples)
163 and tuples[j][4] == cur_deprecated
Adam Vartanian11796722018-02-02 14:09:14 +0000164 and tuples[j][5] == cur_note
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000165 and tuples[j][0] == cur_algorithm):
166 j += 1
167 rowspan = j - i
168 if rowspan > 1:
Adam Vartanian11796722018-02-02 14:09:14 +0000169 output.append(' <td rowspan="%d">%s</td>' % (rowspan, nowrap(cur_algorithm)))
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000170 else:
Adam Vartanian11796722018-02-02 14:09:14 +0000171 output.append(' <td>%s</td>' % nowrap(cur_algorithm))
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000172 if row[1] != cur_mode:
173 cur_mode = row[1]
174 j = i + 1
175 while (j < len(tuples)
176 and tuples[j][4] == cur_deprecated
Adam Vartanian11796722018-02-02 14:09:14 +0000177 and tuples[j][5] == cur_note
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000178 and tuples[j][0] == cur_algorithm
179 and tuples[j][1] == cur_mode):
180 j += 1
181 rowspan = j - i
Adam Vartanian11796722018-02-02 14:09:14 +0000182 modestring = '<br>'.join([nowrap(x) for x in cur_mode])
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000183 if rowspan > 1:
Adam Vartanian748190c2017-03-20 11:16:44 +0000184 output.append(' <td rowspan="%d">%s</td>' % (rowspan, modestring))
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000185 else:
Adam Vartanian748190c2017-03-20 11:16:44 +0000186 output.append(' <td>%s</td>' % modestring)
Adam Vartanian11796722018-02-02 14:09:14 +0000187 output.append(' <td>%s</td>' % '<br>'.join([nowrap(x) for x in row[2]]))
188 output.append(' <td>%s</td>' % nowrap(row[3]))
189 if show_notes:
190 output.append(' <td>%s</td>' % row[5])
Adam Vartanian748190c2017-03-20 11:16:44 +0000191 output.append(' </tr>')
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000192 i += 1
Adam Vartanian748190c2017-03-20 11:16:44 +0000193 output.append(' </tbody>')
194 output.append('</table>')
Adam Vartanian22051132017-05-04 12:11:30 +0100195 elif category['name'].endswith('.Supported'):
196 # Some categories come with a "Supported" and "Enabled" list, and we
197 # group those together in one table for display. Every entry that's enabled
198 # must be supported, so we can just look up the enabled version for each
199 # supported item
200 basename = category['name'][:-len('.Supported')]
201 supported = sort_by_name(category['algorithms'])
202 enabled = sort_by_name(find_by_name(categories, basename + '.Enabled')['algorithms'])
203 output.append('<h3 id="Supported{0}">{0}</h3>'.format(basename))
204 output.append('<table>')
205 output.append(' <thead>')
206 output.append(' <tr>')
207 output.append(' <th>Algorithm</th>')
208 output.append(' <th>Supported API Levels</th>')
209 output.append(' <th>Enabled By Default</th>')
Adam Vartanian11796722018-02-02 14:09:14 +0000210 if show_notes:
211 output.append(' <th>Notes</th>')
Adam Vartanian22051132017-05-04 12:11:30 +0100212 output.append(' </tr>')
213 output.append(' </thead>')
214 output.append(' <tbody>')
215 for algorithm in supported:
216 if 'deprecated' in algorithm and algorithm['deprecated']:
217 output.append(' <tr class="deprecated">')
218 else:
219 output.append(' <tr>')
Adam Vartanian11796722018-02-02 14:09:14 +0000220 output.append(' <td>%s</td>' % nowrap(algorithm['name']))
221 output.append(' <td>%s</td>' % nowrap(algorithm['supported_api_levels']))
Adam Vartanian22051132017-05-04 12:11:30 +0100222 enabled_alg = find_by_name(enabled, algorithm['name'])
223 if enabled_alg is None:
224 output.append(' <td></td>')
225 else:
Adam Vartanian11796722018-02-02 14:09:14 +0000226 output.append(' <td>%s</td>' % nowrap(enabled_alg['supported_api_levels']))
227 if show_notes:
228 if 'note' in algorithm:
229 output.append(' <td>%s</td>' % algorithm['note'])
230 else:
231 output.append(' <td></td>')
Adam Vartanian22051132017-05-04 12:11:30 +0100232 output.append(' </tr>')
233 output.append(' </tbody>')
234 output.append('</table>')
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000235 else:
Adam Vartanian748190c2017-03-20 11:16:44 +0000236 output.append('<h3 id="Supported{name}">{name}</h3>'.format(**category))
237 output.append('<table>')
238 output.append(' <thead>')
239 output.append(' <tr>')
240 output.append(' <th>Algorithm</th>')
241 output.append(' <th>Supported API Levels</th>')
Adam Vartanian11796722018-02-02 14:09:14 +0000242 if show_notes:
243 output.append(' <th>Notes</th>')
Adam Vartanian748190c2017-03-20 11:16:44 +0000244 output.append(' </tr>')
245 output.append(' </thead>')
246 output.append(' <tbody>')
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000247 algorithms = sort_by_name(category['algorithms'])
248 for algorithm in algorithms:
Adam Vartanianbefc86a2017-03-16 14:26:08 +0000249 if 'deprecated' in algorithm and algorithm['deprecated']:
Adam Vartanian748190c2017-03-20 11:16:44 +0000250 output.append(' <tr class="deprecated">')
251 else:
252 output.append(' <tr>')
Adam Vartanian11796722018-02-02 14:09:14 +0000253 output.append(' <td>%s</td>' % nowrap(algorithm['name']))
254 output.append(' <td>%s</td>' % nowrap(algorithm['supported_api_levels']))
255 if show_notes:
256 if 'note' in algorithm:
257 output.append(' <td>%s</td>' % algorithm['note'])
258 else:
259 output.append(' <td></td>')
Adam Vartanian748190c2017-03-20 11:16:44 +0000260 output.append(' </tr>')
261 output.append(' </tbody>')
262 output.append('</table>')
263 if args.for_javadoc:
264 for i in range(len(output)):
265 output[i] = ' * ' + output[i]
266 print '\n'.join(output)
Adam Vartanian9ae0b402017-03-08 16:39:31 +0000267
268
269if __name__ == '__main__':
270 main()