58cb79b920f7d6840581db0244710868155c69be
[nepi.git] / src / nepi / util / parser.py
1 #
2 #    NEPI, a framework to manage network experiments
3 #    Copyright (C) 2013 INRIA
4 #
5 #    This program is free software: you can redistribute it and/or modify
6 #    it under the terms of the GNU General Public License as published by
7 #    the Free Software Foundation, either version 3 of the License, or
8 #    (at your option) any later version.
9 #
10 #    This program 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
13 #    GNU General Public License for more details.
14 #
15 #    You should have received a copy of the GNU General Public License
16 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
19
20 from nepi.design.box import Box
21
22 from xml.dom import minidom
23 import sys
24
25 STRING = "string"
26 BOOL = "bool"
27 INTEGER = "integer"
28 DOUBLE = "float"
29
30 def xmlencode(s):
31     if isinstance(s, str):
32         rv = s.decode("latin1")
33     elif not isinstance(s, unicode):
34         rv = unicode(s)
35     else:
36         rv = s
37     return rv.replace(u'\x00',u'&#0000;')
38
39 def xmldecode(s):
40     return s.replace(u'&#0000',u'\x00').encode("utf8")
41
42 def from_type(value):
43     if isinstance(value, str):
44         return STRING
45     if isinstance(value, bool):
46         return BOOL
47     if isinstance(value, int):
48         return INTEGER
49     if isinstance(value, float):
50         return DOUBLE
51
52 def to_type(type, value):
53     if type == STRING:
54         return str(value)
55     if type == BOOL:
56         return value == "True"
57     if type == INTEGER:
58         return int(value)
59     if type == DOUBLE:
60         return float(value)
61
62 class XMLParser(object):
63     def to_xml(self, box):
64         doc = minidom.Document()
65
66         root = doc.createElement("boxes")
67         doc.appendChild(root)
68
69         traversed = dict()
70         self._traverse_boxes(doc, traversed, box)
71
72         # Keep the order
73         for guid in sorted(traversed.keys()):
74             bnode = traversed[guid]
75             root.appendChild(bnode)
76        
77         try:
78             xml = doc.toprettyxml(indent="    ", encoding="UTF-8")
79         except:
80             print >>sys.stderr, "Oops: generating XML from %s" % (data,)
81             raise
82         
83         return xml
84
85     def _traverse_boxes(self, doc, traversed, box):
86         bnode = doc.createElement("box")
87         bnode.setAttribute("guid", xmlencode(box.guid))
88         bnode.setAttribute("label", xmlencode(box.label))
89         bnode.setAttribute("x", xmlencode(box.x))
90         bnode.setAttribute("y", xmlencode(box.y))
91         bnode.setAttribute("width", xmlencode(box.width))
92         bnode.setAttribute("height", xmlencode(box.height))
93
94         traversed[box.guid] = bnode
95
96         anode = doc.createElement("attributes")
97         bnode.appendChild(anode)
98         for name in sorted(box.attributes):
99             value = getattr(box.a, name)
100             aanode = doc.createElement("attribute")
101             anode.appendChild(aanode)
102             aanode.setAttribute("name", xmlencode(name))
103             aanode.setAttribute("value", xmlencode(value))
104             aanode.setAttribute("type", from_type(value))
105
106         tnode = doc.createElement("tags")
107         bnode.appendChild(tnode)
108         for tag in sorted(box.tags):
109             ttnode = doc.createElement("tag")
110             tnode.appendChild(ttnode)
111             ttnode.setAttribute("name", xmlencode(tag))
112
113         cnode = doc.createElement("connections")
114         bnode.appendChild(cnode)
115         for b in sorted(box.connections):
116             ccnode = doc.createElement("connection")
117             cnode.appendChild(ccnode)
118             ccnode.setAttribute("guid", xmlencode(b.guid))
119             if b.guid not in traversed:
120                 self._traverse_boxes(doc, traversed, b)
121
122     def from_xml(self, xml):
123         doc = minidom.parseString(xml)
124         bnode_list = doc.getElementsByTagName("box")
125
126         boxes = dict()
127         connections = dict()
128
129         for bnode in bnode_list:
130             if bnode.nodeType == doc.ELEMENT_NODE:
131                 guid = int(bnode.getAttribute("guid"))
132                 label = xmldecode(bnode.getAttribute("label"))
133                 x = float(bnode.getAttribute("x"))
134                 y = float(bnode.getAttribute("y"))
135                 height = float(bnode.getAttribute("height"))
136                 width = float(bnode.getAttribute("width"))
137                 box = Box(label=label, guid=guid)
138                 boxes[guid] = box
139
140                 anode_list = bnode.getElementsByTagName("attribute") 
141                 for anode in anode_list:
142                     name = xmldecode(anode.getAttribute("name"))
143                     value = xmldecode(anode.getAttribute("value"))
144                     type = xmldecode(anode.getAttribute("type"))
145                     value = to_type(type, value)
146                     setattr(box.a, name, value)
147                     
148                 tnode_list = bnode.getElementsByTagName("tag") 
149                 for tnode in tnode_list:
150                     value = xmldecode(tnode.getAttribute("name"))
151                     box.tadd(value)
152
153                 connections[box] = set()
154                 cnode_list = bnode.getElementsByTagName("connection")
155                 for cnode in cnode_list:
156                     guid = int(cnode.getAttribute("guid"))
157                     connections[box].add(guid)
158
159         for box, conns in connections.iteritems():
160             for guid in conns:
161                 b = boxes[guid]
162                 box.connect(b)
163
164         return box
165