1use crate::petri_net::PetriNet;
2use xml::writer::{EmitterConfig, EventWriter, Result as XmlResult, XmlEvent};
3
4const XML_PNML_DEFAULT_NAMESPACE: &str = "http://www.pnml.org/version-2009/grammar/pnml";
5const XML_PNML_DEFAULT_GRAMMAR: &str = "http://www.pnml.org/version-2009/grammar/ptnet";
6
7impl PetriNet {
8 pub fn to_pnml_string(&self) -> Result<String, std::io::Error> {
14 let mut writer = Vec::new();
15 self.to_pnml(&mut writer)?;
16 String::from_utf8(writer).map_err(|_|
17 std::io::Error::new(
19 std::io::ErrorKind::Other,
20 "Could not convert the string to UTF-8",
21 ))
22 }
23
24 pub fn to_pnml<T>(&self, writer: &mut T) -> Result<(), std::io::Error>
31 where
32 T: std::io::Write,
33 {
34 self.write_pnml(writer).map_err(|_| {
35 std::io::Error::new(
38 std::io::ErrorKind::Other,
39 "Could not convert the net to PNML",
40 )
41 })
42 }
43
44 fn write_pnml<T>(&self, writer: &mut T) -> XmlResult<()>
50 where
51 T: std::io::Write,
52 {
53 let mut xml_writer = EmitterConfig::new()
54 .perform_indent(true)
55 .create_writer(writer);
56
57 xml_writer.write(XmlEvent::start_element("pnml").default_ns(XML_PNML_DEFAULT_NAMESPACE))?;
59 xml_writer.write(
60 XmlEvent::start_element("net")
61 .attr("id", "net0")
62 .attr("type", XML_PNML_DEFAULT_GRAMMAR),
63 )?;
64 xml_writer.write(XmlEvent::start_element("page").attr("id", "page0"))?;
65
66 self.write_pnml_places(&mut xml_writer)?;
67 self.write_pnml_transitions(&mut xml_writer)?;
68 self.write_pnml_arcs(&mut xml_writer)?;
69
70 xml_writer.write(XmlEvent::end_element())?;
72 xml_writer.write(XmlEvent::end_element())?;
73 xml_writer.write(XmlEvent::end_element())?;
74 Ok(())
75 }
76
77 fn write_pnml_places<T>(&self, writer: &mut EventWriter<T>) -> XmlResult<()>
80 where
81 T: std::io::Write,
82 {
83 for (place_ref, place) in self.places_iter() {
84 let place_xml_element = XmlEvent::start_element("place").attr("id", place_ref.label());
85 writer.write(place_xml_element)?;
86 Self::label_to_pnml(place_ref.label(), writer)?;
87 Self::marking_to_pnml(place.marking(), writer)?;
88 writer.write(XmlEvent::end_element())?;
89 }
90 Ok(())
91 }
92
93 fn write_pnml_transitions<T>(&self, writer: &mut EventWriter<T>) -> XmlResult<()>
96 where
97 T: std::io::Write,
98 {
99 for (transition_ref, _) in self.transitions_iter() {
100 let transition_xml_element =
101 XmlEvent::start_element("transition").attr("id", transition_ref.label());
102 writer.write(transition_xml_element)?;
103 Self::label_to_pnml(transition_ref.label(), writer)?;
104 writer.write(XmlEvent::end_element())?;
105 }
106 Ok(())
107 }
108
109 fn write_pnml_arcs<T>(&self, writer: &mut EventWriter<T>) -> XmlResult<()>
112 where
113 T: std::io::Write,
114 {
115 let arcs = self.find_arcs_place_transition();
116 for (place_ref, transition_ref) in arcs {
117 Self::write_arc(place_ref.label(), transition_ref.label(), writer)?;
118 }
119
120 let arcs = self.find_arcs_transition_place();
121 for (transition_ref, place_ref) in arcs {
122 Self::write_arc(transition_ref.label(), place_ref.label(), writer)?;
123 }
124
125 Ok(())
126 }
127
128 fn write_arc<T>(
131 source: &String,
132 dest: &String,
133 xml_writer: &mut EventWriter<T>,
134 ) -> XmlResult<()>
135 where
136 T: std::io::Write,
137 {
138 let arc_label = format!("({source}, {dest})");
139 let start_element = XmlEvent::start_element("arc")
140 .attr("source", source)
141 .attr("target", dest)
142 .attr("id", &arc_label);
143 xml_writer.write(start_element)?;
144 Self::label_to_pnml(&arc_label, xml_writer)?;
145 xml_writer.write(XmlEvent::start_element("inscription"))?;
146 xml_writer.write(XmlEvent::start_element("text"))?;
147 xml_writer.write(XmlEvent::Characters("1"))?;
149 xml_writer.write(XmlEvent::end_element())?;
150 xml_writer.write(XmlEvent::end_element())?;
151 xml_writer.write(XmlEvent::end_element())?;
152 Ok(())
153 }
154
155 fn label_to_pnml<T>(name: &str, xml_writer: &mut EventWriter<T>) -> XmlResult<()>
158 where
159 T: std::io::Write,
160 {
161 xml_writer.write(XmlEvent::start_element("name"))?;
162 xml_writer.write(XmlEvent::start_element("text"))?;
163 xml_writer.write(XmlEvent::Characters(name))?;
164 xml_writer.write(XmlEvent::end_element())?;
165 xml_writer.write(XmlEvent::end_element())?;
166 Ok(())
167 }
168
169 fn marking_to_pnml<T>(marking: usize, xml_writer: &mut EventWriter<T>) -> XmlResult<()>
172 where
173 T: std::io::Write,
174 {
175 if marking == 0 {
176 return Ok(());
177 }
178 xml_writer.write(XmlEvent::start_element("initialMarking"))?;
179 xml_writer.write(XmlEvent::start_element("text"))?;
180 xml_writer.write(XmlEvent::Characters(&marking.to_string()))?;
181 xml_writer.write(XmlEvent::end_element())?;
182 xml_writer.write(XmlEvent::end_element())?;
183 Ok(())
184 }
185}
186
187#[cfg(test)]
188mod pnml_tests {
189 use super::*;
190 use crate::export::test_export_examples::*;
191 use crate::net_creator::*;
192
193 #[test]
194 fn pnml_string_empty_net() {
195 let net = PetriNet::new();
196 let result = net.to_pnml_string();
197
198 assert!(result.is_ok());
199 assert_eq!(result.unwrap(), PNML_STRING_EMPTY_NET);
200 }
201
202 #[test]
203 fn pnml_string_only_empty_places_net() {
204 let (net, _, _) = create_basic_unconnected_net(5, 0);
205 let result = net.to_pnml_string();
206
207 assert!(result.is_ok());
208 assert_eq!(result.unwrap(), PNML_STRING_ONLY_EMPTY_PLACES_NET);
209 }
210
211 #[test]
212 fn pnml_string_marked_places_net() {
213 let mut net = PetriNet::new();
214 let p1 = net.add_place("P1");
215 let p2 = net.add_place("P2");
216 let p3 = net.add_place("P3");
217 let p4 = net.add_place("P4");
218 let p5 = net.add_place("P5");
219
220 assert!(net.add_token(&p1, 5).is_ok());
221 assert!(net.add_token(&p2, 6).is_ok());
222 assert!(net.add_token(&p3, 3).is_ok());
223 assert!(net.add_token(&p4, 2).is_ok());
224 assert!(net.add_token(&p5, 1).is_ok());
225 let result = net.to_pnml_string();
226
227 assert!(result.is_ok());
228 assert_eq!(result.unwrap(), PNML_STRING_MARKED_PLACES_NET);
229 }
230
231 #[test]
232 fn pnml_string_only_empty_transitions_net() {
233 let (net, _, _) = create_basic_unconnected_net(0, 5);
234 let result = net.to_pnml_string();
235
236 assert!(result.is_ok());
237 assert_eq!(result.unwrap(), PNML_STRING_ONLY_EMPTY_TRANSITIONS_NET);
238 }
239
240 #[test]
241 fn pnml_string_net_with_chain_topology() {
242 let (net, _, _) = create_net_chain_topology(3);
243 let result = net.to_pnml_string();
244
245 assert!(result.is_ok());
246 assert_eq!(result.unwrap(), PNML_STRING_NET_WITH_CHAIN_TOPOLOPY);
247 }
248
249 #[test]
250 fn pnml_string_net_with_loop_topology() {
251 let (net, _, _) = create_net_loop_topology();
252 let result = net.to_pnml_string();
253
254 assert!(result.is_ok());
255 assert_eq!(result.unwrap(), PNML_STRING_NET_WITH_LOOP_TOPOLOGY);
256 }
257}