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