1- #------------------------------------------------------------------------------#
2- # hsd-python: package for manipulating HSD-formatted data in Python #
3- # Copyright (C) 2011 - 2021 DFTB+ developers group #
4- # Licensed under the BSD 2-clause license. #
5- #------------------------------------------------------------------------------#
1+ #-------------------------------------------------------------------------------------------------- #
2+ # hsd-python: package for manipulating HSD-formatted data in Python #
3+ # Copyright (C) 2011 - 2021 DFTB+ developers group #
4+ # Licensed under the BSD 2-clause license. #
5+ #-------------------------------------------------------------------------------------------------- #
66#
77"""
88Contains an event-driven builder for dictionary based (JSON-like) structure
99"""
1010import re
1111from typing import List , Tuple , Union
12- from hsd .common import np , ATTRIB_SUFFIX , HSD_ATTRIB_SUFFIX , HsdError ,\
12+ from hsd .common import HSD_ATTRIB_NAME , np , ATTRIB_SUFFIX , HSD_ATTRIB_SUFFIX , HsdError ,\
1313 QUOTING_CHARS , SPECIAL_CHARS
1414from hsd .eventhandler import HsdEventHandler , HsdEventPrinter
1515
@@ -42,14 +42,16 @@ class HsdDictBuilder(HsdEventHandler):
4242
4343 Args:
4444 flatten_data: Whether multiline data in the HSD input should be
45- flattened into a single list. Othewise a list of lists is created,
46- with one list for every line (default).
47- include_hsd_attribs: Whether the HSD-attributes (processing related
48- attributes, like original tag name, line information, etc.) should
49- be stored.
45+ flattened into a single list. Othewise a list of lists is created, with one list for
46+ every line (default).
47+ lower_tag_names: Whether tag names should be all converted to lower case (to ease case
48+ insensitive processing). Default: False. If set and include_hsd_attribs is also set,
49+ the original tag names can be retrieved from the "name" hsd attributes.
50+ include_hsd_attribs: Whether the HSD-attributes (processing related attributes, like
51+ original tag name, line information, etc.) should be stored (default: False).
5052 """
5153
52- def __init__ (self , flatten_data : bool = False ,
54+ def __init__ (self , flatten_data : bool = False , lower_tag_names : bool = False ,
5355 include_hsd_attribs : bool = False ):
5456 super ().__init__ ()
5557 self ._hsddict : dict = {}
@@ -58,6 +60,7 @@ def __init__(self, flatten_data: bool = False,
5860 self ._data : Union [None , _DataType ] = None
5961 self ._attribs : List [Tuple [str , dict ]] = []
6062 self ._flatten_data : bool = flatten_data
63+ self ._lower_tag_names : bool = lower_tag_names
6164 self ._include_hsd_attribs : bool = include_hsd_attribs
6265
6366
@@ -79,35 +82,49 @@ def open_tag(self, tagname, attrib, hsdattrib):
7982 def close_tag (self , tagname ):
8083 attrib , hsdattrib = self ._attribs .pop (- 1 )
8184 parentblock = self ._parentblocks .pop (- 1 )
85+ key = tagname .lower () if self ._lower_tag_names else tagname
8286 prevcont = parentblock .get (tagname )
83- if prevcont is not None :
84- if isinstance (prevcont , dict ) and self ._data is None :
85- prevcont = [prevcont ]
86- parentblock [tagname ] = prevcont
87- elif not (isinstance (prevcont , list )
88- and isinstance (prevcont [0 ], dict )):
89- msg = f"Invalid duplicate occurance of node '{ tagname } '"
90- raise HsdError (msg )
91-
92- if prevcont is None :
93- content = self ._data if self ._data is not None else self ._curblock
94- parentblock [tagname ] = content
95- if attrib :
96- parentblock [tagname + ATTRIB_SUFFIX ] = attrib
97- if self ._include_hsd_attribs :
98- parentblock [tagname + HSD_ATTRIB_SUFFIX ] = hsdattrib
87+
88+ if self ._data is not None :
89+ if prevcont is None :
90+ parentblock [key ] = self ._data
91+ elif isinstance (prevcont , list ) and len (prevcont ) > 0 and isinstance (prevcont [0 ], dict ):
92+ prevcont .append ({None : self ._data })
93+ elif isinstance (prevcont , dict ):
94+ parentblock [key ] = [prevcont , {None : self ._data }]
95+ else :
96+ parentblock [key ] = [{None : prevcont }, {None : self ._data }]
9997 else :
100- prevcont .append (self ._curblock )
101- prevattrib = parentblock .get (tagname + ATTRIB_SUFFIX )
102- if not (prevattrib is None and attrib is None ):
103- msg = f"Duplicate node '{ tagname } ' should not carry attributes"
104- if self ._include_hsd_attribs :
105- prevhsdattrib = parentblock .get (tagname + HSD_ATTRIB_SUFFIX )
98+ if prevcont is None :
99+ parentblock [key ] = self ._curblock
100+ elif isinstance (prevcont , list ) and len (prevcont ) > 0 and isinstance (prevcont [0 ], dict ):
101+ prevcont .append (self ._curblock )
102+ elif isinstance (prevcont , dict ):
103+ parentblock [key ] = [prevcont , self ._curblock ]
104+ else :
105+ parentblock [key ] = [{None : prevcont }, self ._curblock ]
106+
107+ if attrib and prevcont is None :
108+ parentblock [key + ATTRIB_SUFFIX ] = attrib
109+ elif prevcont is not None :
110+ prevattrib = parentblock .get (key + ATTRIB_SUFFIX )
111+ if isinstance (prevattrib , list ):
112+ prevattrib .append (attrib )
113+ else :
114+ parentblock [key + ATTRIB_SUFFIX ] = [prevattrib , attrib ]
115+
116+ if self ._include_hsd_attribs :
117+ if self ._lower_tag_names :
118+ hsdattrib = {} if hsdattrib is None else hsdattrib
119+ hsdattrib [HSD_ATTRIB_NAME ] = tagname
120+ if prevcont is None :
121+ parentblock [key + HSD_ATTRIB_SUFFIX ] = hsdattrib
122+ else :
123+ prevhsdattrib = parentblock .get (key + HSD_ATTRIB_SUFFIX )
106124 if isinstance (prevhsdattrib , list ):
107125 prevhsdattrib .append (hsdattrib )
108126 else :
109- parentblock [tagname + HSD_ATTRIB_SUFFIX ] = [prevhsdattrib ,
110- hsdattrib ]
127+ parentblock [key + HSD_ATTRIB_SUFFIX ] = [prevhsdattrib , hsdattrib ]
111128 self ._curblock = parentblock
112129 self ._data = None
113130
@@ -189,8 +206,12 @@ def walk(self, dictobj):
189206 elif isinstance (value , list ) and value and isinstance (value [0 ], dict ):
190207 for ind , item in enumerate (value ):
191208 hsdattr = hsdattrib [ind ] if hsdattrib else None
192- self ._eventhandler .open_tag (key , None , hsdattr )
193- self .walk (item )
209+ attr = attrib [ind ] if attrib else None
210+ self ._eventhandler .open_tag (key , attr , hsdattr )
211+ if None in item :
212+ self ._eventhandler .add_text (_to_text (item [None ]))
213+ else :
214+ self .walk (item )
194215 self ._eventhandler .close_tag (key )
195216
196217 else :
0 commit comments