Skip to main content

ebi/ebi_framework/
ebi_file_handler.rs

1use super::{
2    ebi_command::{EBI_COMMANDS, EbiCommand},
3    ebi_input::{EbiInput, EbiObjectImporter, EbiTraitImporter},
4    ebi_output::{EbiObjectExporter, EbiOutputType},
5    ebi_trait::FromEbiTraitObject,
6};
7use crate::{
8    ebi_commands::ebi_command_validate::EBI_VALIDATE,
9    ebi_file_handlers::{
10        business_process_model_and_notation::EBI_BUSINESS_PROCESS_MODEL_AND_NOTATION,
11        compressed_event_log::EBI_COMPRESSED_EVENT_LOG,
12        deterministic_finite_automaton::EBI_DETERMINISTIC_FINITE_AUTOMATON,
13        directly_follows_graph::EBI_DIRECTLY_FOLLOWS_GRAPH,
14        directly_follows_model::EBI_DIRECTLY_FOLLOWS_MODEL, event_log_csv::EBI_EVENT_LOG_CSV,
15        event_log_ocel::EBI_EVENT_LOG_OCEL, event_log_python::EBI_EVENT_LOG_PYTHON,
16        event_log_xes::EBI_EVENT_LOG_XES, executions::EBI_EXECUTIONS,
17        finite_language::EBI_FINITE_LANGUAGE,
18        finite_stochastic_language::EBI_FINITE_STOCHASTIC_LANGUAGE,
19        labelled_petri_net::EBI_LABELLED_PETRI_NET,
20        language_of_alignments::EBI_LANGUAGE_OF_ALIGNMENTS, lola_net::EBI_LOLA_NET,
21        petri_net_markup_language::EBI_PETRI_NET_MARKUP_LANGUAGE,
22        portable_document_format::EBI_PORTABLE_DOCUMENT_FORMAT,
23        portable_network_graphics::EBI_PORTABLE_NETWORK_GRAPHCIS, process_tree::EBI_PROCESS_TREE,
24        process_tree_markup_language::EBI_PROCESS_TREE_MARKUP_LANGUAGE,
25        scalable_vector_graphics::EBI_SCALABLE_VECTOR_GRAPHICS,
26        stochastic_business_process_model_and_notation::EBI_STOCHASTIC_BUSINESS_PROCESS_MODEL_AND_NOTATION,
27        stochastic_deterministic_finite_automaton::EBI_STOCHASTIC_DETERMINISTIC_FINITE_AUTOMATON,
28        stochastic_directly_follows_model::EBI_STOCHASTIC_DIRECTLY_FOLLOWS_MODEL,
29        stochastic_labelled_petri_net::EBI_STOCHASTIC_LABELLED_PETRI_NET,
30        stochastic_language_of_alignments::EBI_STOCHASTIC_LANGUAGE_OF_ALIGNMENTS,
31        stochastic_nondeterministic_finite_automaton::EBI_STOCHASTIC_NONDETERMINISTIC_FINITE_AUTOMATON,
32        stochastic_process_tree::EBI_STOCHASTIC_PROCESS_TREE,
33    },
34    ebi_framework::{
35        ebi_command::get_applicable_commands, ebi_input::EbiObjectImporterFallible,
36        ebi_trait::EbiTrait,
37    },
38    prom::java_object_handler::JavaObjectHandler,
39};
40use ebi_objects::{
41    EbiObjectType,
42    anyhow::{Error, Result, anyhow},
43};
44use std::{collections::BTreeSet, fmt::Display, hash::Hash, io::BufRead, str::FromStr};
45
46/**
47 * The order of this list is important: for the "any object" input type and for trait importers,
48 * they are attempted in order. Thus, the more restrictive formats should come first.
49 */
50pub const EBI_FILE_HANDLERS: &'static [EbiFileHandler] = &[
51    EBI_COMPRESSED_EVENT_LOG,
52    EBI_DIRECTLY_FOLLOWS_GRAPH,
53    EBI_DETERMINISTIC_FINITE_AUTOMATON,
54    EBI_DIRECTLY_FOLLOWS_MODEL,
55    EBI_STOCHASTIC_DIRECTLY_FOLLOWS_MODEL,
56    EBI_EXECUTIONS,
57    EBI_STOCHASTIC_BUSINESS_PROCESS_MODEL_AND_NOTATION,
58    EBI_BUSINESS_PROCESS_MODEL_AND_NOTATION,
59    // EBI_EVENT_LOG_OCEL,
60    EBI_EVENT_LOG_XES,
61    EBI_FINITE_LANGUAGE,
62    EBI_FINITE_STOCHASTIC_LANGUAGE,
63    EBI_LABELLED_PETRI_NET,
64    EBI_LANGUAGE_OF_ALIGNMENTS,
65    EBI_LOLA_NET,
66    EBI_PETRI_NET_MARKUP_LANGUAGE,
67    EBI_STOCHASTIC_DETERMINISTIC_FINITE_AUTOMATON,
68    EBI_STOCHASTIC_LABELLED_PETRI_NET,
69    EBI_PROCESS_TREE,
70    EBI_STOCHASTIC_LANGUAGE_OF_ALIGNMENTS,
71    EBI_STOCHASTIC_PROCESS_TREE,
72    EBI_PROCESS_TREE_MARKUP_LANGUAGE,
73    EBI_EVENT_LOG_CSV,
74    EBI_PORTABLE_DOCUMENT_FORMAT,
75    EBI_SCALABLE_VECTOR_GRAPHICS,
76    EBI_PORTABLE_DOCUMENT_FORMAT,
77    EBI_PORTABLE_NETWORK_GRAPHCIS,
78    EBI_STOCHASTIC_NONDETERMINISTIC_FINITE_AUTOMATON,
79    EBI_EVENT_LOG_PYTHON,
80];
81
82#[derive(Clone, Debug)]
83pub struct EbiFileHandler {
84    pub name: &'static str,
85    pub article: &'static str, //a or an
86    pub file_extension: &'static str,
87    pub is_binary: bool,
88    pub format_specification: &'static str,
89    pub validator: Option<fn(&mut dyn BufRead) -> Result<()>>,
90
91    /// This file format can be imported as the given traits.
92    /// The order matters: importers are attempted in order.
93    pub trait_importers: &'static [EbiTraitImporter],
94
95    /// This file format can be imported as the given objects.
96    /// The order matters: importers are attempted in order, fallible importers last.
97    /// These importers should only fail if the parser failed or the underlying reader fails. If there is a conversion involved that may fail, list it under `trait_importers_fallible`.
98    pub object_importers: &'static [EbiObjectImporter],
99
100    /// This file format can be imported as the given objects.
101    /// The order matters: importers are attempted in order, fallible importers last.
102    /// These importers perform a conversion that may fail.
103    pub object_importers_fallible: &'static [EbiObjectImporterFallible],
104
105    /// This file format can be exported to from the given objects.
106    /// The order matters, because if multiple file handlers can export an object, the one that mentions the object earliest is preferred. Should not fail unless the underlying writer yields an error.
107    pub object_exporters: &'static [EbiObjectExporter],
108
109    /// This file format can be exported to from the given objects.
110    /// The order matters, because if multiple file handlers can export an object, the one that mentions the object earliest is preferred.
111    pub object_exporters_fallible: &'static [EbiObjectExporter],
112    pub java_object_handlers: &'static [JavaObjectHandler],
113}
114
115impl EbiFileHandler {
116    pub fn get_applicable_commands(&self) -> BTreeSet<Vec<&'static EbiCommand>> {
117        let mut result = BTreeSet::new();
118
119        for importer in self.trait_importers {
120            result.extend(importer.get_trait().get_applicable_commands());
121        }
122        for importer in self.object_importers {
123            result.extend(get_applicable_commands(&importer.get_type()));
124        }
125        if self.validator.is_some() {
126            result.insert(vec![&EBI_COMMANDS, &EBI_VALIDATE]);
127        }
128
129        result
130    }
131
132    pub fn get_producing_commands(&self) -> BTreeSet<Vec<&'static EbiCommand>> {
133        //get objects that can export to this file handler
134        let mut objects = vec![];
135        for exporter in self.object_exporters {
136            objects.push(exporter.get_type());
137        }
138        for exporter in self.object_exporters_fallible {
139            objects.push(exporter.get_type());
140        }
141
142        //get commands that can output any of the objects
143        let mut result = BTreeSet::new();
144        for command_path in EBI_COMMANDS.get_command_paths() {
145            if let Some(EbiCommand::Command { output_type, .. }) = command_path.last() {
146                if let EbiOutputType::ObjectType(x) = output_type {
147                    if objects.contains(x) {
148                        result.insert(command_path);
149                    }
150                }
151            }
152        }
153
154        result
155    }
156
157    pub fn can_import_as_object(&self, object_type: &EbiObjectType) -> Tri {
158        for importer in self.object_importers {
159            if importer.get_type() == *object_type {
160                return Tri::Yes;
161            }
162        }
163        for importer in self.object_importers_fallible {
164            if importer.get_type() == *object_type {
165                return Tri::Fallible;
166            }
167        }
168        return Tri::No;
169    }
170
171    pub fn can_import_as_trait(&self, trait_type: &EbiTrait) -> bool {
172        for importer in self.trait_importers {
173            if importer.get_trait() == *trait_type {
174                return true;
175            }
176        }
177        return false;
178    }
179
180    pub fn can_export_as_object(&self, object_type: &EbiObjectType) -> Tri {
181        for exporter in self.object_exporters {
182            if exporter.get_type() == *object_type {
183                return Tri::Yes;
184            }
185        }
186        for exporter in self.object_exporters_fallible {
187            if exporter.get_type() == *object_type {
188                return Tri::Fallible;
189            }
190        }
191        return Tri::No;
192    }
193}
194
195impl FromStr for EbiFileHandler {
196    type Err = Error;
197
198    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
199        for file_handler in EBI_FILE_HANDLERS {
200            if file_handler.validator.is_some()
201                && (file_handler.name == s || file_handler.file_extension == s)
202            {
203                return Ok(file_handler.clone());
204            }
205        }
206        return Err(anyhow!("{} is not an Ebi file handler.", s));
207    }
208}
209
210impl FromEbiTraitObject for EbiFileHandler {
211    fn from_trait_object(object: EbiInput) -> Result<Box<Self>> {
212        match object {
213            EbiInput::FileHandler(e) => Ok(Box::new(e)),
214            _ => Err(anyhow!(
215                "cannot read {} {} as an file handler",
216                object.get_type().get_article(),
217                object.get_type()
218            )),
219        }
220    }
221}
222
223impl Eq for EbiFileHandler {}
224
225impl PartialEq for EbiFileHandler {
226    fn eq(&self, other: &Self) -> bool {
227        self.name == other.name
228    }
229}
230
231impl PartialOrd for EbiFileHandler {
232    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
233        self.name
234            .to_lowercase()
235            .partial_cmp(&other.name.to_lowercase())
236    }
237}
238
239impl Ord for EbiFileHandler {
240    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
241        self.name.to_lowercase().cmp(&other.name.to_lowercase())
242    }
243}
244
245impl Hash for EbiFileHandler {
246    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
247        self.name.hash(state);
248    }
249}
250
251impl Display for EbiFileHandler {
252    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253        write!(f, "{} (.{})", self.name, self.file_extension)
254    }
255}
256
257pub fn get_file_handlers(object_type: &EbiObjectType) -> Vec<&'static EbiFileHandler> {
258    let mut result = vec![];
259    for file_handler in EBI_FILE_HANDLERS.iter() {
260        for importer in file_handler.object_importers {
261            if &importer.get_type() == object_type {
262                result.push(file_handler);
263                break;
264            }
265        }
266    }
267    result
268}
269
270pub fn get_file_handlers_fallible(object_type: &EbiObjectType) -> Vec<&'static EbiFileHandler> {
271    let mut result = vec![];
272    for file_handler in EBI_FILE_HANDLERS.iter() {
273        for importer in file_handler.object_importers_fallible {
274            if &importer.get_type() == object_type {
275                result.push(file_handler);
276                break;
277            }
278        }
279    }
280    result
281}
282
283pub enum Tri {
284    Yes,
285    No,
286    Fallible,
287}
288
289impl Tri {
290    pub fn to_latex_circle(&self) -> &str {
291        match self {
292            Tri::Yes => "\\CIRCLE",
293            Tri::Fallible => "\\LEFTcircle",
294            Tri::No => "\\Circle",
295        }
296    }
297}
298
299#[cfg(test)]
300mod tests {
301    use std::str::FromStr;
302
303    use crate::{
304        ebi_file_handlers::{
305            executions::EBI_EXECUTIONS, finite_stochastic_language::EBI_FINITE_STOCHASTIC_LANGUAGE,
306            process_tree::EBI_PROCESS_TREE,
307            stochastic_labelled_petri_net::EBI_STOCHASTIC_LABELLED_PETRI_NET,
308        },
309        ebi_framework::{
310            ebi_file_handler::EbiFileHandler, ebi_input::EbiInput, ebi_trait::FromEbiTraitObject,
311        },
312    };
313
314    #[test]
315    fn file_handlers() {
316        assert_eq!(
317            EbiFileHandler::from_str("slang").unwrap(),
318            EBI_FINITE_STOCHASTIC_LANGUAGE
319        );
320        assert!(EbiFileHandler::from_str("blablabla44252435").is_err());
321
322        EbiFileHandler::get_producing_commands(&EBI_PROCESS_TREE);
323        EbiFileHandler::get_producing_commands(&EBI_STOCHASTIC_LABELLED_PETRI_NET);
324
325        assert!(
326            EBI_PROCESS_TREE
327                .cmp(&EBI_STOCHASTIC_LABELLED_PETRI_NET)
328                .is_lt()
329        );
330
331        EbiFileHandler::from_trait_object(EbiInput::FileHandler(EBI_EXECUTIONS)).unwrap();
332    }
333}