Salome HOME
begin fix sat test
[tools/sat.git] / src / xmlManager.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2013  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 import os
20 import shutil
21
22 try: # For python2
23     import sys
24     reload(sys)  
25     sys.setdefaultencoding('utf8')
26 except:
27     pass
28
29 import src
30 from . import ElementTree as etree
31
32 verbose = False
33
34 class XmlLogFile(object):
35     '''Class to manage writing in salomeTools xml log file
36     '''
37     def __init__(self, filePath, rootname, attrib = {}):
38         '''Initialization
39         
40         :param filePath str: The path to the file where to write the log file
41         :param rootname str: The name of the root node of the xml file
42         :param attrib dict: the dictionary that contains the attributes 
43                             and value of the root node
44         '''
45         # Initialize the filePath and ensure that the directory 
46         # that contain the file exists (make it if necessary)
47         self.logFile = filePath
48         src.ensure_path_exists(os.path.dirname(filePath))
49         # Initialize the field that contain the xml in memory
50         self.xmlroot = etree.Element(rootname, attrib = attrib)
51
52     def write_tree(self, stylesheet=None, file_path = None):
53         '''Write the xml tree in the log file path. Add the stylesheet if asked.
54         
55         :param stylesheet str: The stylesheet to apply to the xml file
56         '''
57         log_file_path = self.logFile
58         if file_path:
59             log_file_path = file_path
60         try:
61             f = open(log_file_path, 'w')
62             f.write("<?xml version='1.0' encoding='utf-8'?>\n")
63             if stylesheet:
64                 f.write("<?xml-stylesheet type='text/xsl' href='%s'?>\n" %  stylesheet)
65             f.write(etree.tostring(self.xmlroot, encoding='utf-8'))
66             f.close()
67         except IOError:
68             pass  
69         
70     def add_simple_node(self, node_name, text=None, attrib={}):
71         '''Add a node with some attibutes and text to the root node.
72         
73         :param node_name str: the name of the node to add
74         :param text str: the text of the node
75         :param attrib dict: the dictionary containing the 
76                             attribute of the new node
77         '''
78         n = etree.Element(node_name, attrib=attrib)
79         n.text = text
80         self.xmlroot.append(n)
81         return n
82     
83     def append_node_text(self, node_name, text):
84         '''Append a new text to the node that has node_name as name
85         
86         :param node_name str: The name of the node on which append text
87         :param text str: The text to append
88         '''
89         # find the corresponding node
90         for field in self.xmlroot:
91             if field.tag == node_name:
92                 # append the text
93                 field.text += text
94
95     def append_node_attrib(self, node_name, attrib):
96         '''Append a new attributes to the node that has node_name as name
97         
98         :param node_name str: The name of the node on which append text
99         :param attrib dixt: The attrib to append
100         '''
101         self.xmlroot.find(node_name).attrib.update(attrib)
102
103 class ReadXmlFile(object):
104     '''Class to manage reading of an xml log file
105     '''
106     def __init__(self, filePath):
107         '''Initialization
108         
109         :param filePath str: The xml file to be read
110         '''
111         self.filePath = filePath
112         etree_inst = etree.parse(filePath)
113         self.xmlroot = etree_inst.parse(filePath)
114
115     def getRootAttrib(self):
116         '''Get the attibutes of the self.xmlroot
117         
118         :return: The attributes of the root node
119         :rtype: dict
120         '''
121         return self.xmlroot.attrib
122     
123     def get_attrib(self, node_name):
124         '''Get the attibutes of the node node_name in self.xmlroot
125         
126         :param node_name str: the name of the node
127         :return: the attibutes of the node node_name in self.xmlroot
128         :rtype: dict
129         '''
130         attrib = self.xmlroot.find(node_name).attrib
131         # To be python 3 compatible, convert bytes to str if there are any
132         fixedAttrib = {}
133         for k in attrib.keys():
134             if isinstance(k, bytes):
135                 key = k.decode()
136             else:
137                 key = k
138             if isinstance(attrib[k], bytes):
139                 value = attrib[k].decode()
140             else:
141                 value = attrib[k]
142             fixedAttrib[key] = value
143         return fixedAttrib
144     
145     def get_node_text(self, node):
146         '''Get the text of the first node that has name 
147            that corresponds to the parameter node
148         
149         :param node str: the name of the node from which get the text
150         :return: the text of the first node that has name 
151                  that corresponds to the parameter node
152         :rtype: str
153         '''
154         return self.xmlroot.find(node).text
155     
156 def add_simple_node(root_node, node_name, text=None, attrib={}):
157     '''Add a node with some attibutes and text to the root node.
158
159     :param root_node etree.Element: the Etree element where to add the new node    
160     :param node_name str: the name of the node to add
161     :param text str: the text of the node
162     :param attrib dict: the dictionary containing the 
163                         attribute of the new node
164     '''
165     n = etree.Element(node_name, attrib=attrib)
166     n.text = text
167     root_node.append(n)
168     return n
169
170 def append_node_attrib(root_node, attrib):
171     '''Append a new attributes to the node that has node_name as name
172     
173     :param root_node etree.Element: the Etree element 
174                                     where to append the new attibutes
175     :param attrib dixt: The attrib to append
176     '''
177     root_node.attrib.update(attrib)
178
179 def find_node_by_attrib(xmlroot, name_node, key, value):
180     '''Find the nfirst ode from xmlroot that has name name_node and that has in 
181        its attributes {key : value}. Return the node
182     
183     :param xmlroot etree.Element: the Etree element where to search
184     :param name_node str: the name of node to search
185     :param key str: the key to search
186     :param value str: the value to search
187     :return: the found node
188     :rtype: xmlroot etree.Element
189     '''
190     l_nodes =  xmlroot.findall(name_node)
191     for node in l_nodes:
192         if key not in node.attrib.keys():
193             continue
194         if node.attrib[key] == value:
195             return node
196     return None
197     
198
199 def write_report(filename, xmlroot, stylesheet):
200     """Writes a report file from a XML tree.
201     
202     :param filename str: The path to the file to create
203     :param xmlroot etree.Element: the Etree element to write to the file
204     :param stylesheet str: The stylesheet to add to the begin of the file
205     """
206     dirname = os.path.dirname(filename)
207     if not os.path.exists(dirname):
208       os.makedirs(dirname)
209     if len(stylesheet) > 0:
210        styleName = stylesheet
211     else:
212        styleName = None
213
214     with open(filename, "w") as f:
215       f.write("<?xml version='1.0' encoding='utf-8'?>\n")
216       if styleName is not None:
217         f.write("<?xml-stylesheet type='text/xsl' href='%s'?>\n" % styleName)
218       f.write(etree.tostring(xmlroot, encoding='utf-8'))
219
220     # create fileStyle in dirname if not existing
221     if styleName is not None:
222       styleFile = os.path.join(dirname, styleName)
223       if not os.path.exists(styleFile):
224         # copy if from "salomeTools/src/xsl"
225         srcdir = os.path.dirname(src.__file__)
226         srcFile = os.path.join(srcdir, "xsl", styleName)
227         if verbose: print("write_report %s style %s" % (srcFile, styleFile))
228         shutil.copy(srcFile, dirname)
229
230 def escapeSequence(aStr):
231     """
232     See xml specification:
233     The ampersand character(&) and the left angle bracket(<) MUST NOT appear in their
234     literal form, except when used as markup delimiters, or within a comment, a processing
235     instruction, or a CDATA section.
236     If they are needed elsewhere, they MUST be escaped using either numeric character references
237     or the strings '&amp;' and '&lt;' respectively.
238     The right angle bracket(>) may be
239     represented using the string '&gt;', and MUST,
240     for compatibility, be escaped using either '&gt;' or a character reference
241     when it appears in the string " ]]> " in content,
242     when that string is not marking the end of a CDATA section.
243     You can use these escape sequences:
244     < (less - than) as &#60; or &lt;
245     > (greater - than) as &#62; or &gt;
246     & (ampersand) as &#38;
247     ' (apostrophe or single quote) as &#39;
248     " (double-quote) as &#34;
249     """
250     replaces = [ ('&', '&amp;'),
251                  ('>', '&gt;'),
252                  ('<', '&lt;'),
253                  ("'", '&#39;'),
254                  ('"', '&#34;'),
255                 ]
256     res = aStr
257     for ini, fin in replaces: # order matters
258       res = res.replace(ini, fin)
259     return res
260
261