1#!/usr/bin/env python
2#
3# Copyright (C) 2018 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"""A tool for inserting values from the build system into a manifest or a test config."""
18
19from __future__ import print_function
20from xml.dom import minidom
21
22
23android_ns = 'http://schemas.android.com/apk/res/android'
24
25
26def get_children_with_tag(parent, tag_name):
27  children = []
28  for child in  parent.childNodes:
29    if child.nodeType == minidom.Node.ELEMENT_NODE and \
30       child.tagName == tag_name:
31      children.append(child)
32  return children
33
34
35def find_child_with_attribute(element, tag_name, namespace_uri,
36                              attr_name, value):
37  for child in get_children_with_tag(element, tag_name):
38    attr = child.getAttributeNodeNS(namespace_uri, attr_name)
39    if attr is not None and attr.value == value:
40      return child
41  return None
42
43
44def parse_manifest(doc):
45  """Get the manifest element."""
46
47  manifest = doc.documentElement
48  if manifest.tagName != 'manifest':
49    raise RuntimeError('expected manifest tag at root')
50  return manifest
51
52
53def ensure_manifest_android_ns(doc):
54  """Make sure the manifest tag defines the android namespace."""
55
56  manifest = parse_manifest(doc)
57
58  ns = manifest.getAttributeNodeNS(minidom.XMLNS_NAMESPACE, 'android')
59  if ns is None:
60    attr = doc.createAttributeNS(minidom.XMLNS_NAMESPACE, 'xmlns:android')
61    attr.value = android_ns
62    manifest.setAttributeNode(attr)
63  elif ns.value != android_ns:
64    raise RuntimeError('manifest tag has incorrect android namespace ' +
65                       ns.value)
66
67
68def parse_test_config(doc):
69  """ Get the configuration element. """
70
71  test_config = doc.documentElement
72  if test_config.tagName != 'configuration':
73    raise RuntimeError('expected configuration tag at root')
74  return test_config
75
76
77def as_int(s):
78  try:
79    i = int(s)
80  except ValueError:
81    return s, False
82  return i, True
83
84
85def compare_version_gt(a, b):
86  """Compare two SDK versions.
87
88  Compares a and b, treating codenames like 'Q' as higher
89  than numerical versions like '28'.
90
91  Returns True if a > b
92
93  Args:
94    a: value to compare
95    b: value to compare
96  Returns:
97    True if a is a higher version than b
98  """
99
100  a, a_is_int = as_int(a.upper())
101  b, b_is_int = as_int(b.upper())
102
103  if a_is_int == b_is_int:
104    # Both are codenames or both are versions, compare directly
105    return a > b
106  else:
107    # One is a codename, the other is not.  Return true if
108    # b is an integer version
109    return b_is_int
110
111
112def get_indent(element, default_level):
113  indent = ''
114  if element is not None and element.nodeType == minidom.Node.TEXT_NODE:
115    text = element.nodeValue
116    indent = text[:len(text)-len(text.lstrip())]
117  if not indent or indent == '\n':
118    # 1 indent = 4 space
119    indent = '\n' + (' ' * default_level * 4)
120  return indent
121
122
123def write_xml(f, doc):
124  f.write('<?xml version="1.0" encoding="utf-8"?>\n')
125  for node in doc.childNodes:
126    f.write(node.toxml(encoding='utf-8') + '\n')
127