Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / tools / metrics / histograms / update_histogram_enum.py
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Updates enums in histograms.xml file with values read from provided C++ enum.
6
7 If the file was pretty-printed, the updated version is pretty-printed too.
8 """
9
10 import logging
11 import os
12 import print_style
13 import re
14 import sys
15
16 from xml.dom import minidom
17
18 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
19 from diff_util import PromptUserToAcceptDiff
20
21 class UserError(Exception):
22   def __init__(self, message):
23     Exception.__init__(self, message)
24
25   @property
26   def message(self):
27     return self.args[0]
28
29
30 def Log(message):
31   logging.info(message)
32
33
34 def ReadHistogramValues(filename, start_marker, end_marker):
35   """Reads in values from |filename|, returning a list of (label, value) pairs
36   corresponding to the enum framed by |start_marker| and |end_marker|.
37   """
38   # Read the file as a list of lines
39   with open(filename) as f:
40     content = f.readlines()
41
42   START_REGEX = re.compile(start_marker)
43   ITEM_REGEX = re.compile(r'^(\w+)')
44   ITEM_REGEX_WITH_INIT = re.compile(r'(\w+)\s*=\s*(\d+)')
45   END_REGEX = re.compile(end_marker)
46
47   # Locate the enum definition and collect all entries in it
48   inside_enum = False # We haven't found the enum definition yet
49   result = {}
50   for line in content:
51     line = line.strip()
52     if inside_enum:
53       # Exit condition: we reached last enum value
54       if END_REGEX.match(line):
55         inside_enum = False
56       else:
57         # Inside enum: generate new xml entry
58         m = ITEM_REGEX_WITH_INIT.match(line)
59         if m:
60           enum_value = int(m.group(2))
61           label = m.group(1)
62         else:
63           m = ITEM_REGEX.match(line)
64           if m:
65             label = m.group(1)
66           else:
67             continue
68         result[enum_value] = label
69         enum_value += 1
70     else:
71       if START_REGEX.match(line):
72         inside_enum = True
73         enum_value = 0
74   return result
75
76
77 def CreateEnumItemNode(document, value, label):
78   """Creates an int element to append to an enum."""
79   item_node = document.createElement('int')
80   item_node.attributes['value'] = str(value)
81   item_node.attributes['label'] = label
82   return item_node
83
84
85 def UpdateHistogramDefinitions(histogram_enum_name, source_enum_values,
86                                source_enum_path, document):
87   """Updates the enum node named |histogram_enum_name| based on the definition
88   stored in |source_enum_values|. Existing items for which |source_enum_values|
89   doesn't contain any corresponding data will be preserved. |source_enum_path|
90   will be used to insert a comment.
91   """
92   # Get a dom of <enum name=|name| ...> node in |document|.
93   for enum_node in document.getElementsByTagName('enum'):
94     if enum_node.attributes['name'].value == histogram_enum_name:
95       break
96   else:
97     raise UserError('No {0} enum node found'.format(name))
98
99   new_children = []
100
101   # Add a "Generated from (...)" comment.
102   new_children.append(
103       document.createComment(' Generated from {0} '.format(source_enum_path)))
104
105   # Scan existing nodes in |enum_node| and build |new_children|.
106   # - For each int node in |enum_node|, if there is a corresponding entry in
107   #   |source_enum_values|, drop the existing node and add a node newly created
108   #   from |source_enum_values| to |new_children|.
109   # - Drop existing "Generated from (...)" comment in |enum_node|.
110   # - Copy anything else.
111   SOURCE_COMMENT_REGEX = re.compile('^ Generated from ')
112   for child in enum_node.childNodes:
113     if child.nodeName == 'int':
114       value = int(child.attributes['value'].value)
115       if source_enum_values.has_key(value):
116         new_children.append(
117             CreateEnumItemNode(document, value, source_enum_values[value]))
118         del source_enum_values[value]
119       else:
120         new_children.append(child)
121     # Drop existing source comments if any.
122     elif (child.nodeType != minidom.Node.COMMENT_NODE or
123           SOURCE_COMMENT_REGEX.match(child.data) is None):
124       new_children.append(child)
125
126   # Add remaining entries i.e. new enum values, in the |source_enum_values| to
127   # the |new_children|.
128   for value in sorted(source_enum_values.iterkeys()):
129     new_children.append(
130         CreateEnumItemNode(document, value, source_enum_values[value]))
131
132   # Update |enum_node|.
133   while enum_node.hasChildNodes():
134     enum_node.removeChild(enum_node.lastChild)
135   for child in new_children:
136     enum_node.appendChild(child)
137
138
139 def UpdateHistogramEnum(histogram_enum_name, source_enum_path,
140                         start_marker, end_marker):
141   """Updates |histogram_enum_name| enum in histograms.xml file with values
142   read from |source_enum_path|, where |start_marker| and |end_marker| indicate
143   the beginning and end of the source enum definition, respectively.
144   """
145   # TODO(ahernandez.miralles): The line below is present in nearly every
146   # file in this directory; factor out into a central location
147   HISTOGRAMS_PATH = 'histograms.xml'
148
149   Log('Reading histogram enum definition from "{0}".'.format(source_enum_path))
150   source_enum_values = ReadHistogramValues(source_enum_path, start_marker,
151                                            end_marker)
152
153   Log('Reading existing histograms from "{0}".'.format(HISTOGRAMS_PATH))
154   with open(HISTOGRAMS_PATH, 'rb') as f:
155     histograms_doc = minidom.parse(f)
156     f.seek(0)
157     xml = f.read()
158
159   Log('Comparing histograms enum with new enum definition.')
160   UpdateHistogramDefinitions(histogram_enum_name, source_enum_values,
161                              source_enum_path, histograms_doc)
162
163   Log('Writing out new histograms file.')
164   new_xml = print_style.GetPrintStyle().PrettyPrintNode(histograms_doc)
165   if not PromptUserToAcceptDiff(
166       xml, new_xml, 'Is the updated version acceptable?'):
167     Log('Cancelled.')
168     return
169
170   with open(HISTOGRAMS_PATH, 'wb') as f:
171     f.write(new_xml)
172
173   Log('Done.')