'''
Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v1.0 which accompanies this distribution,
and is available at http://www.eclipse.org/legal/epl-v10.html
Created on May 21, 2014
@author: Vaclav Demcak
'''
from xml.dom.minidom import Element
KEY_NOT_FOUND = '' # KeyNotFound for dictDiff
class XMLtoDictParserTools():
@staticmethod
def parseDOM_ToDict( node, returnedDict = None, ignoreList = [] ):
"""
Return Dictionary representation of the xml DOM Element.
Repeated tags are put to the array sorted by key (id or order)
otherwise is the value represented by tag key name.
@param node: DOM Element
@param returnedDict : dictionary (default value None)
@param ignereList : list of ignored tags for the xml DOM Element
(default value is empty list)
@return: dict representation for the input DOM Element
"""
returnedDict = {} if returnedDict is None else returnedDict
if ( node.nodeType == Element.ELEMENT_NODE ) :
nodeKey = ( node.localName ).encode( 'utf-8', 'ignore' )
if nodeKey not in ignoreList :
if node.childNodes is not None :
childDict = {}
for child in node.childNodes :
if child.nodeType == Element.TEXT_NODE :
nodeValue = ( child.nodeValue ).encode( 'utf-8', 'ignore' )
if ( len( nodeValue.strip( ' \t\n\r' ) ) ) > 0 :
XMLtoDictParserTools.addDictValue( returnedDict, nodeKey, nodeValue )
nodeKey = None
break
elif child.nodeType == Element.ELEMENT_NODE :
childDict = XMLtoDictParserTools.parseDOM_ToDict( child, childDict, ignoreList )
XMLtoDictParserTools.addDictValue( returnedDict, nodeKey, childDict )
return returnedDict
@staticmethod
def addDictValue( m_dict, key, value ):
'''
Method add key and value to input dict. If the dict
contain the key, we are creating array for the values
and sort array by sort_key ('order' tag or '*-id' tag )
@param m_dict: dictionary for key and value
@param key: dict key
@param value: dict value
'''
if key is not None :
if ( isinstance( value, str ) ) :
# we need to predict possible differences
# for same value in upper or lower case
value = value.lower()
if key not in m_dict :
m_dict[key] = value
else :
exist_value = m_dict.get( key )
if ( type( exist_value ) is dict ) :
list_values = [exist_value, value]
key_for_sort = XMLtoDictParserTools.searchKey( exist_value )
if key_for_sort is not None :
list_values = sorted( list_values, key = lambda k: k[key_for_sort] )
m_dict[key] = list_values
elif ( isinstance( exist_value, list ) ) :
exist_value.append( value )
list_values = exist_value
key_for_sort = XMLtoDictParserTools.searchKey( value )
if key_for_sort is not None :
list_values = sorted( list_values, key = lambda k: k[key_for_sort] )
m_dict[key] = list_values
else :
m_dict[key] += value
@staticmethod
def searchKey( dictionary ):
"""
Return an order key for the array ordering. OF_13
allows only two possible kind of the order keys
'order' or '*-id'
@param dictionary: dictionary with data
@return: the array order key
"""
subKeyStr = ['-id', 'order']
for substr in subKeyStr :
for key in dictionary:
if key == substr :
return key
elif key.endswith( substr ):
return key
return None
@staticmethod
def getDifferenceDict( original_dict, responded_dict ):
"""
Return a dict of keys that differ with another config object. If a value is
not found in one fo the configs, it will be represented by KEY_NOT_FOUND.
@param original_dict: Fist dictionary to diff.
@param responded_dict: Second dictionary to diff.
@return diff: Dict of Key => (original_dict.val, responded_dict.val)
Dict of Key => (original_key, KEY_NOT_FOUND)
Dict of Key => (KEY_NOT_FOUND, original_key)
"""
diff = {}
# Check all keys in original_dict dict
for key in original_dict.keys():
if ( not responded_dict.has_key( key ) ):
# missing key in responded dict
diff[key] = ( key, KEY_NOT_FOUND )
# check values of the dictionaries
elif ( original_dict[key] != responded_dict[key] ):
# values are not the same #
orig_dict_val = original_dict[key]
resp_dict_val = responded_dict[key]
# check value is instance of dictionary
if isinstance( orig_dict_val, dict ) and isinstance( resp_dict_val, dict ):
sub_dif = XMLtoDictParserTools.getDifferenceDict( orig_dict_val, resp_dict_val )
if sub_dif :
diff[key] = sub_dif
# check value is instance of list
# TODO - > change a basic comparator to compare by id or order
elif isinstance( orig_dict_val, list ) and isinstance( resp_dict_val, list ) :
sub_list_diff = {}
# the list lengths
orig_i, resp_i = len( orig_dict_val ), len( resp_dict_val )
# define a max iteration length (less from both)
min_index = orig_i if orig_i < resp_i else resp_i
for index in range ( 0, min_index, 1 ) :
if ( orig_dict_val[index] != resp_dict_val[index] ) :
if isinstance( orig_dict_val, dict ) and isinstance( resp_dict_val, dict ) :
sub_list_diff[index] = ( XMLtoDictParserTools.getDifferenceDict( orig_dict_val[index], resp_dict_val[index] ) )
else :
sub_list_diff[index] = ( orig_dict_val[index], resp_dict_val[index] )
if ( orig_i > min_index ) :
# original is longer as responded dict
for index in range ( min_index, orig_i, 1 ):
sub_list_diff[index] = ( orig_dict_val[index], None )
elif ( resp_i > min_index ) :
# responded dict is longer as original
for index in range ( min_index, resp_i, 1 ) :
sub_list_diff[index] = ( None, resp_dict_val[index] )
if sub_list_diff :
diff[key] = sub_list_diff
else :
diff[key] = ( original_dict[key], responded_dict[key] )
# Check all keys in responded_dict dict to find missing
for key in responded_dict.keys():
if ( not original_dict.has_key( key ) ):
diff[key] = ( KEY_NOT_FOUND, key )
return diff