blob: 996b82dc46f1dc24e8c73b8e5e3afb13b6d7a89a [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from blinkbuild.name_style_converter import NameStyleConverter
import json5_generator
import template_expander
import make_style_builder
import keyword_utils
from name_utilities import enum_key_for_css_keyword
def _find_continuous_segment(numbers):
"""Find the longest continuous segment in a list of numbers.
For example:
input:
1, 2, 3, 4, 5, 6
22,70,23,24,25,26
output:
number_list_sorted:
1, 3, 4, 5, 6, 2
22,23,24,25,26,70
segments:
0, 1, 5, 6
which means there are 3 segment with start and end indices
on the number_list_sorted to be: (0, 1), (1, 5), (5, 6)
Args:
numbers: List of pairs of number
Returns:
segment: a list containing the indices of the segment start point
and end point.
number_list: sorted by the first element version of the input.
"""
segments = [0]
number_list_sorted = sorted(numbers, key=lambda elem: elem[0])
for i in range(len(number_list_sorted) - 1):
# continuous segment is a segment which the number in pair is 1 unit
# more than the previous pair
if (number_list_sorted[i + 1][0] - number_list_sorted[i][0] != 1 or
number_list_sorted[i + 1][1] - number_list_sorted[i][1] != 1):
segments.append(i + 1)
segments.append(len(number_list_sorted))
return segments, number_list_sorted
def _find_largest_segment(segments):
"""Find the largest segment given a list of start and end
indices of segments
Args:
segments: a list of start and end indices
Returns:
longest_segment: the start and end indices of the longest segment
"""
segment_list = zip(segments[:-1], segments[1:])
return max(segment_list, key=lambda x: x[1] - x[0])
def _find_enum_longest_continuous_segment(property_,
name_to_position_dictionary):
"""Find the longest continuous segment in the list of keywords
Finding the continuous segment will allows us to do the subtraction
between keywords so that the distance between 2 keywords in this
enum is equal to the distance of corresponding keywords in another
enum.
Step 1:
Convert keyword enums into number.
Sort and find all continuous segment in the list of enums.
Step 2:
Get the longest segment.
Step 3:
Compose a list of keyword enums and their respective numbers
in the sorted order.
Step 4:
Build the switch case statements of other enums not in the
segment. Enums in the segment will be computed in default clause.
"""
property_enum_order = range(len(property_['keywords']))
css_enum_order = [
name_to_position_dictionary[x] for x in property_['keywords']
]
enum_pair_list = zip(css_enum_order, property_enum_order)
enum_segment, enum_pair_list = _find_continuous_segment(enum_pair_list)
longest_segment = _find_largest_segment(enum_segment)
enum_tuple_list = []
for x in enum_pair_list:
keyword = NameStyleConverter(property_['keywords'][x[1]])
enum_tuple_list.append((enum_key_for_css_keyword(keyword), x[1], x[0]))
return enum_tuple_list, enum_segment, longest_segment
class CSSValueIDMappingsWriter(make_style_builder.StyleBuilderWriter):
def __init__(self, json5_file_paths, output_dir):
super(CSSValueIDMappingsWriter, self).__init__(json5_file_paths,
output_dir)
self._outputs = {
'css_value_id_mappings_generated.h':
self.generate_css_value_mappings,
}
self.css_values_dictionary_file = json5_file_paths[3]
css_properties = self.css_properties.longhands
# We sort the enum values based on each value's position in
# the keywords as listed in css_properties.json5. This will ensure that if there is a continuous
# segment in css_properties.json5 matching the segment in this enum then
# the generated enum will have the same order and continuity as
# css_properties.json5 and we can get the longest continuous segment.
# Thereby reduce the switch case statement to the minimum.
css_properties = keyword_utils.sort_keyword_properties_by_canonical_order(
css_properties, json5_file_paths[3], self.default_parameters)
@template_expander.use_jinja(
'core/css/templates/css_value_id_mappings_generated.h.tmpl')
def generate_css_value_mappings(self):
mappings = {}
include_paths = set()
css_values_dictionary = json5_generator.Json5File.load_from_files(
[self.css_values_dictionary_file],
default_parameters=self.default_parameters).name_dictionaries
name_to_position_dictionary = dict(
zip([x['name'].original for x in css_values_dictionary],
range(len(css_values_dictionary))))
for property_ in self.css_properties.properties_including_aliases:
include_paths.update(property_['include_paths'])
if property_['field_template'] == 'multi_keyword':
mappings[property_['type_name']] = {
'default_value':
property_['default_value'],
'mapping': [
enum_key_for_css_keyword(k)
for k in property_['keywords']
],
}
elif property_['field_template'] == 'keyword':
enum_pair_list, enum_segment, p_segment = _find_enum_longest_continuous_segment(
property_, name_to_position_dictionary)
mappings[property_['type_name']] = {
'default_value': property_['default_value'],
'mapping': enum_pair_list,
'segment': enum_segment,
'longest_segment_length': p_segment[1] - p_segment[0],
'start_segment': enum_pair_list[p_segment[0]],
'end_segment': enum_pair_list[p_segment[1] - 1],
}
return {
'include_paths': list(sorted(include_paths)),
'input_files': self._input_files,
'mappings': mappings,
}
if __name__ == '__main__':
json5_generator.Maker(CSSValueIDMappingsWriter).main()