2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Transforms direct Java class references in Android layout .xml files
7 according to the specified JarJar rules."""
13 from xml
.dom
import minidom
15 from util
import build_utils
18 class JarJarRules(object):
19 def __init__(self
, jarjar_rules
):
21 for line
in jarjar_rules
.splitlines():
26 if src
.endswith('**'):
27 src_real_name
= src
[:-2]
32 if dest
.endswith('@0'):
33 self
._rules
.append((src
, dest
[:-2] + src_real_name
))
34 elif dest
.endswith('@1'):
36 self
._rules
.append((src
, dest
[:-2]))
38 assert not '@' in dest
39 self
._rules
.append((src
, dest
))
41 def RenameClass(self
, class_name
):
42 for old
, new
in self
._rules
:
43 if old
.endswith('**') and old
[:-2] in class_name
:
44 return class_name
.replace(old
[:-2], new
, 1)
45 if '*' not in old
and class_name
.endswith(old
):
46 return class_name
.replace(old
, new
, 1)
50 def RenameNodes(node
, rules
):
51 if node
.nodeType
== node
.ELEMENT_NODE
:
52 if node
.tagName
.lower() == 'view' and node
.attributes
.has_key('class'):
53 node
.attributes
['class'] = rules
.RenameClass(node
.attributes
['class'])
55 node
.tagName
= rules
.RenameClass(node
.tagName
)
56 for child
in node
.childNodes
:
57 RenameNodes(child
, rules
)
60 def ProcessLayoutFile(path
, rules
):
61 xmldoc
= minidom
.parse(path
)
62 RenameNodes(xmldoc
.documentElement
, rules
)
63 with
open(path
, 'w') as f
:
67 def LayoutFilesFilter(src
, names
):
68 if os
.path
.basename(src
).lower() != 'layout':
71 return filter(lambda n
: n
.endswith('.xml'), names
)
74 def ProcessResources(options
):
75 with
open(options
.rules_path
) as f
:
76 rules
= JarJarRules(f
.read())
78 build_utils
.DeleteDirectory(options
.output_dir
)
79 for input_dir
in options
.input_dir
:
80 shutil
.copytree(input_dir
, options
.output_dir
)
82 for root
, _dirnames
, filenames
in os
.walk(options
.output_dir
):
83 layout_files
= LayoutFilesFilter(root
, filenames
)
84 for layout_file
in layout_files
:
85 ProcessLayoutFile(os
.path
.join(root
, layout_file
), rules
)
89 parser
= optparse
.OptionParser()
90 parser
.add_option('--input-dir', action
='append',
91 help='Path to the resources folder to process.')
92 parser
.add_option('--output-dir',
93 help=('Directory to hold processed resources. Note: the ' +
94 'directory will be clobbered on every invocation.'))
95 parser
.add_option('--rules-path',
96 help='Path to the jarjar rules file.')
97 parser
.add_option('--stamp', help='Path to touch on success.')
99 options
, args
= parser
.parse_args()
102 parser
.error('No positional arguments should be given.')
104 # Check that required options have been provided.
105 required_options
= ('input_dir', 'output_dir', 'rules_path')
106 build_utils
.CheckOptions(options
, parser
, required
=required_options
)
112 options
= ParseArgs()
114 ProcessResources(options
)
117 build_utils
.Touch(options
.stamp
)
120 if __name__
== '__main__':