blob: d8f5581aa9c0f5c8189a71502d9ec2b306a0e7cf [file] [log] [blame]
# Copyright (c) 2020, The Linux Foundation. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of The Linux Foundation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import argparse
import os
import xml.etree.ElementTree as ET
ATTRIBUTE_ORDER = ['name', 'id', 'value']
XML_HEAD = """<?xml version="1.0" encoding="ISO-8859-1"?>"""
COPY_RIGHT = "<!--- copy right -->"
def getCopyRight():
global COPY_RIGHT
script_dir = os.path.dirname(os.path.abspath(__file__))
copyright_path = os.path.join(script_dir, "copyright.txt")
# print(copyright_path)
f = open(copyright_path, 'r')
COPY_RIGHT = f.read()
f.close()
def write_xml_root_to_file_v2(file_path, super_root):
for sub_node in super_root:
mixer = sub_node
data = gen_xml_string(mixer)
full_xml = XML_HEAD+'\n'+COPY_RIGHT+'\n'+data
with open(file_path, 'w') as f:
f.write(full_xml)
def xml_to_map(xml_node, root_key=None, map=None, node_level=0):
"""Given xml node, generate map(dict)"""
if not map:
map = dict()
if not root_key:
current_key = get_key_for_node_only(xml_node, node_level)
else:
current_key = root_key + '->' + get_key_for_node_only(
xml_node, node_level)
map[current_key] = xml_node
for sub_node in xml_node:
xml_to_map(sub_node, current_key, map, node_level + 1)
return map
def get_mixer_map(super_root):
for node in super_root:
mixer = node
mixer_map = dict()
for node in mixer:
node_key = gen_xml_string(node)
mixer_map[node_key] = node
return mixer_map
def get_key_for_node_only(xml_node, level):
""" Given xml node ,generate unique based on (node, node atrributes, node depth) only"""
attr_str = ''
for attrib_name in sorted(xml_node.keys()):
attr_str += attrib_name.strip() + "=" + xml_node.get(
attrib_name).strip() + ":"
if attr_str != '':
attr_str = attr_str[:-1]
node_str = 'level=' + str(level) + ':' + xml_node.tag
key_str = node_str + ":" + attr_str
if xml_node.text != None and not xml_node.text.isspace():
key_str = key_str + ":" + xml_node.text.strip()
return key_str
def get_copy_xml_node(xml_node):
""" Return Exact copy of xml node, return type as Element """
new_attrib = dict()
for at_name, at_value in xml_node.attrib.items():
new_attrib[at_name.strip()] = at_value.strip()
copy_node = ET.Element(xml_node.tag.strip(), new_attrib)
if xml_node.text != None and not xml_node.text.isspace:
copy_node.text = xml_node.text
return copy_node
def copy_full_node(xml_node):
""" Return Exact copy whole xml node including sub nodes, return type as Element """
new_node = get_copy_xml_node(xml_node)
for sub_node in xml_node:
new_node.append(copy_full_node(sub_node))
return new_node
def open_xml_root(filename):
""" open xml filename add SuperRoot Element at top, return SuperRoot Node (Element) """
try:
xml_tree = ET.parse(filename)
xml_root = xml_tree.getroot()
super_root = ET.Element("SuperRoot")
super_root.append(xml_root)
super_root = copy_full_node(super_root)
except:
print('unable to open: '+filename+' as xml')
raise
return super_root
def gen_xml_string(xml_node, level=0):
"""Generate xml string for a given xml node with good indentation"""
s = '<' + xml_node.tag + ' '
if xml_node.attrib:
for at_name in ATTRIBUTE_ORDER:
at_value = xml_node.attrib.get(at_name, "ZEBRAAAA")
if at_value != "ZEBRAAAA":
s += at_name + '=\"' + at_value + '\" '
space_str = ''
for i in range(level):
space_str += ' '
if len(xml_node) > 0 or xml_node.text != None:
s = s.strip()+'>\n'
for sub_node in xml_node:
s += space_str + ' ' + gen_xml_string(sub_node,
level + 1) + '\n'
if xml_node.text:
s += space_str + ' ' + xml_node.text + '\n'
s += space_str + '</' + xml_node.tag + '>'
else:
s = s.strip()+'/>'
return s
def print_map(map):
for key, value in map.items():
print(key, value)
def arrange_ctl_path_tags(super_root_ug):
super_root = super_root = ET.Element("SuperRoot")
new_mixer = ET.Element("mixer")
super_root.append(new_mixer)
for child in super_root_ug:
mixer = child
for child in mixer:
if child.tag == 'ctl':
new_child = copy_full_node(child)
new_mixer.append(new_child)
for child in mixer:
if child.tag == 'path':
new_child = copy_full_node(child)
new_mixer.append(new_child)
return super_root
def mixer_extract_union(xml_nodes):
map = dict()
super_root = super_root = ET.Element("SuperRoot")
new_mixer = ET.Element("mixer")
super_root.append(new_mixer)
for xml_node in xml_nodes:
for child in xml_node:
mixer = child
for child in mixer:
key = "tag="+child.tag+":"+"name="+child.attrib.get('name', str(None))+":"+"id="\
+ child.attrib.get('id', str(None))
if not map.get(key, False):
new_child = copy_full_node(child)
new_mixer.append(new_child)
map[key] = True
super_root = arrange_ctl_path_tags(super_root)
return super_root
def mixer_extract_base(xml_node1, xml_node2):
""" Assumption of SuperRoot is given for both node"""
map1 = xml_to_map(xml_node1, map=None)
for sub_node in xml_node2:
mixer = sub_node
new_mixer = get_copy_xml_node(mixer)
super_root = get_copy_xml_node(xml_node2)
super_root.append(new_mixer)
level = 0
current_key = get_key_for_node_only(
xml_node2, level) + '->' + get_key_for_node_only(mixer, level + 1)
level += 1
for xml_child in mixer:
if _mixer_extract_base(xml_child, current_key, map1, level + 1):
child_copy = copy_full_node(xml_child)
new_mixer.append(child_copy)
return super_root
def _mixer_extract_base(mixer_child, root_key, base_map, node_level):
current_key = root_key + "->" + get_key_for_node_only(
mixer_child, node_level)
if base_map.get(current_key, 0) == 0:
return False
for child in mixer_child:
return _mixer_extract_base(child, current_key, base_map,
node_level + 1)
return True
def mixer_extract_overlay(super_base, super_mixer):
for child in super_mixer:
mixer = child
base_map = get_mixer_map(super_base)
new_mixer = get_copy_xml_node(mixer)
super_root = get_copy_xml_node(super_mixer)
super_root.append(new_mixer)
for xml_child in mixer:
xml_child_key = gen_xml_string(xml_child)
node_elem=base_map.get(xml_child_key, False)
if not isinstance(node_elem,ET.Element):
child_copy = copy_full_node(xml_child)
new_mixer.append(child_copy)
return super_root
def _mixer_extract_overlay(mixer_child, root_key, base_map, node_level):
current_key = root_key + "->" + get_key_for_node_only(
mixer_child, node_level)
if base_map.get(current_key, 0) == 0:
return False
for child in mixer_child:
if not _mixer_extract_overlay(child, current_key, base_map,
node_level + 1):
return False
return True
def seperate_ctl_path(super_root):
for child in super_root:
mixer = child
path_nodes = list()
for child in mixer:
if child.tag == 'path':
path_nodes.append(child)
for path_node in path_nodes:
mixer.remove(path_node)
for path_node in path_nodes:
mixer.append(path_node)
return super_root
def mixer_combine(super_base, super_overlay):
super_base = copy_full_node(super_base)
super_overlay = copy_full_node(super_overlay)
super_base = seperate_ctl_path(super_base)
super_overlay = seperate_ctl_path(super_overlay)
for child in super_base:
base = child
for child in super_overlay:
overlay = child
base_ctl_len = 0
for child in base:
if child.tag == 'path':
break
base_ctl_len += 1
overlay_ctl_len = 0
for child in overlay:
if child.tag == 'path':
break
overlay_ctl_len += 1
for i in range(overlay_ctl_len):
base.insert(base_ctl_len + i, overlay[i])
for i in range(overlay_ctl_len, len(overlay)):
base.append(overlay[i])
super_base = override_tag(super_base)
super_base = sort_tag_depend(super_base)
return super_base
def similar_tag_exists(mixer, node, node_index):
l = 0
for child in mixer:
l += 1
if l > node_index and child.tag == node.tag:
if child != node \
and child.attrib.get('name', None) == node.attrib.get('name', None) \
and child.attrib.get('id', None) == node.attrib.get('id', None):
return True
return False
def override_tag(super_combined):
for child in super_combined:
mixer = child
child_nodes = list()
l = 0
for child in mixer:
l += 1
if similar_tag_exists(mixer, child, l):
child_nodes.append(child)
for child in child_nodes:
mixer.remove(child)
return super_combined
def sort_tag_depend(super_root):
for node in super_root:
mixer = node
dep_map = dict()
sub_node_list = list()
start_flag = True
start = 0
path_start = 0
all_nodes_map = dict()
for sub_node in mixer:
start += 1
sub_node_list.append(sub_node)
if sub_node.tag == "ctl":
continue
if start_flag:
path_start = start
start_flag = False
key = sub_node.tag+":"+"name=" + \
sub_node.attrib.get("name", str(None)) + "id=" + \
sub_node.attrib.get("id", str(None))
dep_list = list()
all_nodes_map[key] = sub_node
for child in sub_node:
if child.tag == "ctl":
continue
dep_list.append(child)
if len(dep_list) != 0:
dep_map[key] = dep_list
else:
dep_map[key] = True
# print("path_start:"+str(path_start))
ctl_list = sub_node_list[0:path_start]
path_list = sub_node_list[path_start:]
new_path_list = _sort_tag_depend(
all_nodes_map, path_list, dep_map, new_path_list=None)
full_list = ctl_list+new_path_list
mixer.clear()
for node in full_list:
mixer.append(node)
return super_root
def _sort_tag_depend(all_nodes_map, path_list, dep_map, new_path_list=None):
if new_path_list is None:
new_path_list = list()
for sub_node in path_list:
key = sub_node.tag+":"+"name=" + \
sub_node.attrib.get("name", str(None))+"id=" + \
sub_node.attrib.get("id", str(None))
res = dep_map.get(key, False)
req_node = all_nodes_map.get(key, 0)
if req_node == 0:
print(gen_xml_string(sub_node))
print("Error in ordering the mixer tags")
if res == True:
new_path_list.append(req_node)
elif res == False:
continue
else:
_sort_tag_depend(all_nodes_map, res, dep_map, new_path_list)
new_path_list.append(req_node)
dep_map[key] = False
return new_path_list
def is_xmls_good(files):
try:
for file_name in files:
super_root = open_xml_root(file_name)
return True
except:
print('invalid xml file: ' + file_name)
return False
def is_xml_good(file_name):
try:
super_root = open_xml_root(file_name)
print('able to parse:'+file_name+' as xml')
return True
except:
print('unable to parse:'+file_name+' as xml')
raise
#############
def base_gen(args):
l = len(args.files)
file1 = open_xml_root(args.files[0])
for i in range(1, l):
file2 = open_xml_root(args.files[i])
base = mixer_extract_base(file1, file2)
file1 = copy_full_node(base)
if not args.out:
out = 'base.xml'
else:
[out] = args.out
write_xml_root_to_file_v2(os.path.join(args.out_dir, out), file1)
def overlay_gen(args):
l = len(args.files)
base = open_xml_root(args.base)
for i in range(0, l):
file1 = open_xml_root(args.files[i])
overlay = mixer_extract_overlay(base, file1)
overlay = copy_full_node(overlay)
write_xml_root_to_file_v2(os.path.join(
args.out_dir, args.out[i]), overlay)
def combine_gen(args):
l = len(args.overlay)
base = open_xml_root(args.base)
for i in range(0, l):
overlay = open_xml_root(args.overlay[i])
combine = mixer_combine(base, overlay)
combine = copy_full_node(combine)
write_xml_root_to_file_v2(os.path.join(
args.out_dir, args.out[i]), combine)
def union_gen(args):
l = len(args.files)
file1 = open_xml_root(args.files[0])
ul = list()
ul.append(file1)
for i in range(1, l):
file2 = open_xml_root(args.files[i])
ul.append(file2)
file1 = mixer_extract_union(ul)
if not args.out:
out = 'mixer_union.xml'
else:
[out] = args.out
write_xml_root_to_file_v2(os.path.join(args.out_dir, out), file1)
def main(args):
getCopyRight()
if args.check:
check(args)
if args.generate == 'base':
base_gen(args)
if args.generate == 'union':
union_gen(args)
if args.generate == 'overlay':
overlay_gen(args)
if args.generate == 'combine':
combine_gen(args)
def is_valid_file_list(file_path_list):
for file_path in file_path_list:
if not os.path.isfile(file_path):
print(file_path+' doesn\'t exist')
return False
return True
def gen_abs_paths(rel_path_list):
l = list()
for rel_path in rel_path_list:
l.append(os.path.abspath(rel_path))
return l
def validate(args):
if args.check:
if args.file:
return args
if not os.path.isfile(args.file1):
print(args.file1+' is not a file')
return False
if not os.path.isfile(args.file2):
print(args.file1+' is not a file')
return False
return args
if not args.out_dir:
args.out_dir = os.path.dirname(os.path.realpath(__file__))
if not os.path.isdir(args.out_dir):
print('out_dir doesn\'t exist')
return False
args.out_dir = os.path.abspath(args.out_dir)
if args.generate == 'base' or args.generate == 'union':
if not args.files:
print('missing --files argument')
return False
if not is_valid_file_list(args.files):
return False
args.files = gen_abs_paths(args.files)
if not is_xmls_good(args.files):
return False
return args
if args.generate == 'overlay':
if not args.base:
print('missing --base argument')
return False
if not is_valid_file_list([args.base]):
return False
args.base = os.path.abspath(args.base)
if not args.files:
print('missing --files argument')
return False
if not is_valid_file_list(args.files):
return False
args.files = gen_abs_paths(args.files)
if not is_xmls_good([args.base]):
return False
if not is_xmls_good(args.files):
return False
if not args.out:
args.out = list()
for i in range(len(args.files)):
s = 'overlay'+str(i)+'.xml'
args.out.append(s)
elif not len(args.out) == len(args.files):
return False
return args
if args.generate == 'combine':
if not args.base:
print('invalid base')
return False
if not is_valid_file_list([args.base]):
return False
args.base = os.path.abspath(args.base)
if not args.overlay:
print('no overlays')
return False
if not is_valid_file_list(args.overlay):
return False
args.overlay = gen_abs_paths(args.overlay)
if not is_xmls_good([args.base]):
return False
if not is_xmls_good(args.overlay):
return False
if not args.out:
args.out = list()
for i in range(len(args.overlay)):
s = 'combine'+str(i)+'.xml'
args.out.append(s)
elif not len(args.out) == len(args.overlay):
return False
return args
def _check_mixer_equivalent(sub_node, map2, root_key, level):
current_key = root_key + "->" + get_key_for_node_only(sub_node, level)
res = map2.get(current_key, 0)
if res == 0:
return False
for child in sub_node:
if not _check_mixer_equivalent(child, map2, current_key, level + 1):
return False
return True
def check_mixer_equivalent(root1, root2):
for m in root1:
mixer_node1 = m
for m in root2:
mixer_node2 = m
map2 = xml_to_map(mixer_node2)
node_level = 0
current_key = get_key_for_node_only(mixer_node1, node_level)
flag = True
for sub_node in mixer_node1:
if not _check_mixer_equivalent(sub_node, map2, current_key, node_level+1):
print(gen_xml_string(sub_node))
flag = False
return flag
def checker_v3(node1, node2):
for node in node1:
mixer1 = node
for node in node2:
mixer2 = node
map2 = dict()
for sub_node in mixer2:
key = gen_xml_string(sub_node)
map2[key] = True
flag = True
for sub_node in mixer1:
key = gen_xml_string(sub_node)
if not map2.get(key, False):
print(key)
flag = False
return flag
def check(args):
if args.file:
if not os.path.isfile(args.file):
print(args.file+" doesn't exist")
is_xml_good(args.file)
return
f1 = open_xml_root(args.file1)
f2 = open_xml_root(args.file2)
if checker_v3(f1, f2):
print('file1 <= file2')
print("=======================================================")
if checker_v3(f2, f1):
print('file2 <= file1')
return
if __name__ == '__main__':
arg_parser = argparse.ArgumentParser(
description="Script to generate base,overlay or to combining base and overlay")
arg_parser.add_argument('--files', nargs='+',
default=None)
arg_parser.add_argument('--base', action='store', type=str,
default=None)
arg_parser.add_argument('--union', action='store', type=str,
default=None)
arg_parser.add_argument('--overlay', nargs='+',
default=None)
arg_parser.add_argument('--out_dir', action='store', type=str,
default=None)
arg_parser.add_argument('--out', nargs='+',
default=None)
arg_parser.add_argument("--generate", type=str, choices=['base', 'overlay', 'combine', 'union'],
help="choose one among options", default=None)
arg_parser.add_argument('--check', action='store_true', default=False)
arg_parser.add_argument('--file', action='store', type=str, default=None)
arg_parser.add_argument('--file1', action='store', type=str)
arg_parser.add_argument('--file2', action='store', type=str)
arg_parser.add_argument(
'--copyright', action='store', type=str, default=None)
args = arg_parser.parse_args()
args = validate(args)
if not args:
print('xml_opt.py invalid arguments')
else:
main(args)