Salome HOME
add sat log on http.server 8765
[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 import src.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           with open(log_file_path, 'w') as f:
62             f.write("<?xml version='1.0' encoding='utf-8'?>\n")
63             if stylesheet:
64                 # example as href='./hat.xsl' 
65                 # as local file xml with local file xsl
66                 # with 'python3 -m http.server 8765 &' and
67                 # 'chromium-browser http://localhost:8765/hat.xml' or
68                 # 'firefox http://localhost:8765/hat.xml'
69                 f.write("<?xml-stylesheet type='text/xsl' href='./%s'?>\n" %  stylesheet)
70                 pass
71             res= etree.tostring(self.xmlroot, encoding='utf-8')
72             f.write(res)
73         except IOError:
74             pass  
75         
76     def add_simple_node(self, node_name, text=None, attrib={}):
77         '''Add a node with some attibutes and text to the root node.
78         
79         :param node_name str: the name of the node to add
80         :param text str: the text of the node
81         :param attrib dict: the dictionary containing the 
82                             attribute of the new node
83         '''
84         n = etree.Element(node_name, attrib=attrib)
85         n.text = text
86         self.xmlroot.append(n)
87         return n
88     
89     def append_node_text(self, node_name, text):
90         '''Append a new text to the node that has node_name as name
91         
92         :param node_name str: The name of the node on which append text
93         :param text str: The text to append
94         '''
95         # find the corresponding node
96         for field in self.xmlroot:
97             if field.tag == node_name:
98                 # append the text
99                 field.text += text
100
101     def append_node_attrib(self, node_name, attrib):
102         '''Append a new attributes to the node that has node_name as name
103         
104         :param node_name str: The name of the node on which append text
105         :param attrib dixt: The attrib to append
106         '''
107         self.xmlroot.find(node_name).attrib.update(attrib)
108
109 class ReadXmlFile(object):
110     '''Class to manage reading of an xml log file
111     '''
112     def __init__(self, filePath):
113         '''Initialization
114         
115         :param filePath str: The xml file to be read
116         '''
117         self.filePath = filePath
118         etree_inst = etree.parse(filePath)
119         self.xmlroot = etree_inst.parse(filePath)
120
121     def getRootAttrib(self):
122         '''Get the attibutes of the self.xmlroot
123         
124         :return: The attributes of the root node
125         :rtype: dict
126         '''
127         return self.xmlroot.attrib
128     
129     def get_attrib(self, node_name):
130         '''Get the attibutes of the node node_name in self.xmlroot
131         
132         :param node_name str: the name of the node
133         :return: the attibutes of the node node_name in self.xmlroot
134         :rtype: dict
135         '''
136         attrib = self.xmlroot.find(node_name).attrib
137         # To be python 3 compatible, convert bytes to str if there are any
138         fixedAttrib = {}
139         for k in attrib.keys():
140             if isinstance(k, bytes):
141                 key = k.decode()
142             else:
143                 key = k
144             if isinstance(attrib[k], bytes):
145                 value = attrib[k].decode()
146             else:
147                 value = attrib[k]
148             fixedAttrib[key] = value
149         return fixedAttrib
150     
151     def get_node_text(self, node):
152         '''Get the text of the first node that has name 
153            that corresponds to the parameter node
154         
155         :param node str: the name of the node from which get the text
156         :return: the text of the first node that has name 
157                  that corresponds to the parameter node
158         :rtype: str
159         '''
160         return self.xmlroot.find(node).text
161     
162 def add_simple_node(root_node, node_name, text=None, attrib={}):
163     '''Add a node with some attibutes and text to the root node.
164
165     :param root_node etree.Element: the Etree element where to add the new node    
166     :param node_name str: the name of the node to add
167     :param text str: the text of the node
168     :param attrib dict: the dictionary containing the 
169                         attribute of the new node
170     '''
171     n = etree.Element(node_name, attrib=attrib)
172     n.text = text
173     root_node.append(n)
174     return n
175
176 def append_node_attrib(root_node, attrib):
177     '''Append a new attributes to the node that has node_name as name
178     
179     :param root_node etree.Element: the Etree element 
180                                     where to append the new attibutes
181     :param attrib dixt: The attrib to append
182     '''
183     root_node.attrib.update(attrib)
184
185 def find_node_by_attrib(xmlroot, name_node, key, value):
186     '''Find the nfirst ode from xmlroot that has name name_node and that has in 
187        its attributes {key : value}. Return the node
188     
189     :param xmlroot etree.Element: the Etree element where to search
190     :param name_node str: the name of node to search
191     :param key str: the key to search
192     :param value str: the value to search
193     :return: the found node
194     :rtype: xmlroot etree.Element
195     '''
196     l_nodes =  xmlroot.findall(name_node)
197     for node in l_nodes:
198         if key not in node.attrib.keys():
199             continue
200         if node.attrib[key] == value:
201             return node
202     return None
203     
204
205 def write_report(filename, xmlroot, stylesheet):
206     """Writes a report file from a XML tree.
207     
208     :param filename str: The path to the file to create
209     :param xmlroot etree.Element: the Etree element to write to the file
210     :param stylesheet str: The stylesheet to add to the begin of the file
211     """
212     dirname = os.path.dirname(filename)
213     if not os.path.exists(dirname):
214       os.makedirs(dirname)
215     if len(stylesheet) > 0:
216        styleName = stylesheet
217     else:
218        styleName = None
219
220     with open(filename, "w") as f:
221       f.write("<?xml version='1.0' encoding='utf-8'?>\n")
222       if styleName is not None:
223         f.write("<?xml-stylesheet type='text/xsl' href='%s'?>\n" % styleName)
224       res = etree.tostring(xmlroot, encoding='utf-8')
225       # print("********** etree.tostring %s" % res)
226       f.write(res)
227
228     # create fileStyle in dirname if not existing
229     if styleName is not None:
230       styleFile = os.path.join(dirname, styleName)
231       if not os.path.exists(styleFile):
232         # copy if from "salomeTools/src/xsl"
233         srcdir = os.path.dirname(src.__file__)
234         srcFile = os.path.join(srcdir, "xsl", styleName)
235         if verbose: print("write_report %s style %s" % (srcFile, styleFile))
236         shutil.copy(srcFile, dirname)
237
238 def escapeSequence(aStr):
239     """
240     See xml specification:
241     The ampersand character(&) and the left angle bracket(<) MUST NOT appear in their
242     literal form, except when used as markup delimiters, or within a comment, a processing
243     instruction, or a CDATA section.
244     If they are needed elsewhere, they MUST be escaped using either numeric character references
245     or the strings '&amp;' and '&lt;' respectively.
246     The right angle bracket(>) may be
247     represented using the string '&gt;', and MUST,
248     for compatibility, be escaped using either '&gt;' or a character reference
249     when it appears in the string " ]]> " in content,
250     when that string is not marking the end of a CDATA section.
251     You can use these escape sequences:
252     < (less - than) as &#60; or &lt;
253     > (greater - than) as &#62; or &gt;
254     & (ampersand) as &#38;
255     ' (apostrophe or single quote) as &#39;
256     " (double-quote) as &#34;
257     """
258     replaces = [ ('&', '&amp;'),
259                  ('>', '&gt;'),
260                  ('<', '&lt;'),
261                  ("'", '&#39;'),
262                  ('"', '&#34;'),
263                 ]
264     res = aStr
265     for ini, fin in replaces: # order matters
266       res = res.replace(ini, fin)
267     return res
268
269