Line data Source code
1 : // Author(s): Jeroen van der Wulp and Wieger Wesselink
2 : // Copyright: see the accompanying file COPYING or copy at
3 : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING
4 : //
5 : // Distributed under the Boost Software License, Version 1.0.
6 : // (See accompanying file LICENSE_1_0.txt or copy at
7 : // http://www.boost.org/LICENSE_1_0.txt)
8 : //
9 : /// \file mcrl2/data/detail/data_property_map.h
10 : /// \brief A property map containing properties of an LPS specification.
11 :
12 : #ifndef MCRL2_DATA_DETAIL_DATA_PROPERTY_MAP_H
13 : #define MCRL2_DATA_DETAIL_DATA_PROPERTY_MAP_H
14 :
15 : #include "mcrl2/data/variable.h"
16 :
17 : namespace mcrl2
18 : {
19 :
20 : namespace data
21 : {
22 :
23 : namespace detail
24 : {
25 :
26 : /// \brief Base class for storing properties of mCRL2 types.
27 : /// Properties are (key, value) pairs stored as strings in <tt>KEY = VALUE</tt>
28 : /// format. The data_property_map has some predefined functions for
29 : /// types in the Data Library.
30 : /// The optional type argument is used by derived classes. The type
31 : /// represents the name of a derived class as per CRTP.
32 : template < typename Derived = void >
33 : class data_property_map
34 : {
35 : protected:
36 :
37 : /// \brief Add start/end separators for non-set container types
38 : template < typename Container >
39 0 : static std::string add_separators(std::string const& c, typename std::enable_if< atermpp::is_set< Container >::value >::type* = nullptr)
40 : {
41 0 : return "[" + c + "]";
42 : }
43 :
44 : /// \brief Add start/end separators for set container types
45 : template < typename Container >
46 : static std::string add_separators(std::string const& c, typename std::enable_if< !atermpp::is_set< Container >::value >::type* = 0)
47 : {
48 : return "{" + c + "}";
49 : }
50 :
51 : /// \brief Contains a normalized string representation of the properties.
52 : std::map<std::string, std::string> m_data;
53 :
54 : //--------------------------------------------//
55 : // print functions
56 : //--------------------------------------------//
57 444 : std::string print(std::size_t n) const
58 : {
59 444 : std::ostringstream out;
60 444 : out << n;
61 888 : return out.str();
62 444 : }
63 :
64 22 : std::string print(std::string s) const
65 : {
66 22 : return s;
67 : }
68 :
69 174 : std::string print(const core::identifier_string& s) const
70 : {
71 174 : return s;
72 : }
73 :
74 97 : std::string print(const data::variable& v) const
75 : {
76 97 : return data::pp(v) + ":" + data::pp(v.sort());
77 : }
78 :
79 : template < typename Container >
80 509 : std::string print(const Container& v, typename atermpp::enable_if_container< Container >::type* = nullptr) const
81 : {
82 509 : std::set<std::string> elements;
83 :
84 1034 : for (auto i = v.begin(); i != v.end(); ++i)
85 : {
86 525 : elements.insert(static_cast< Derived const& >(*this).print(*i));
87 : }
88 :
89 1018 : return utilities::string_join(elements, ", ");
90 509 : }
91 :
92 : template < typename Container >
93 456 : std::string print(const Container& v, bool print_separators, typename atermpp::enable_if_container< Container >::type* = nullptr) const
94 : {
95 456 : return (print_separators) ? add_separators< Container >(print(v)) : print(v);
96 : }
97 :
98 : //--------------------------------------------//
99 : // parse functions
100 : //--------------------------------------------//
101 30 : unsigned int parse_unsigned_int(std::string const& text) const
102 : {
103 30 : return std::stoul(utilities::remove_whitespace(text)); // Transform string to an unsigned integer.
104 : }
105 :
106 122 : std::set<std::string> parse_set_string(std::string const& text) const
107 : {
108 244 : std::vector<std::string> v = utilities::split(text, ",");
109 122 : std::for_each(v.begin(), v.end(), utilities::trim);
110 244 : return std::set<std::string>(v.begin(), v.end());
111 122 : }
112 :
113 2 : std::set<std::multiset<std::string> > parse_set_multiset_string(std::string const& text) const
114 : {
115 2 : std::set<std::multiset<std::string> > result;
116 4 : std::vector<std::string> multisets = utilities::split(text, ";");
117 16 : for (const std::string& ms: multisets)
118 : {
119 28 : std::string s = utilities::regex_replace("[{}]", "", ms);
120 28 : std::vector<std::string> v = utilities::split(s, ",");
121 14 : std::for_each(v.begin(), v.end(), utilities::trim);
122 14 : result.insert(std::multiset<std::string>(v.begin(), v.end()));
123 : }
124 4 : return result;
125 2 : }
126 :
127 : //--------------------------------------------//
128 : // compare functions
129 : //--------------------------------------------//
130 : /// \brief Compares two integers, and returns a message if they are different.
131 : /// If if they are equal the empty string is returned.
132 15 : std::string compare(const std::string& property, unsigned int x, unsigned int y) const
133 : {
134 15 : if (x != y)
135 : {
136 4 : std::ostringstream out;
137 4 : out << "Difference in property " << property << " detected: " << x << " versus " << y << "\n";
138 4 : return out.str();
139 4 : }
140 11 : return "";
141 : }
142 :
143 : /// \brief Compares two sets and returns a string with the differences encountered.
144 : /// Elements present in the first one but not in the second are printed with a '+'
145 : /// in front of it, elements present in the seconde but not in the first one with a '-'
146 : /// in front of it. A value x of the type T is printed using print(x), so this
147 : /// operation must be defined.
148 : /// If no differences are found the empty string is returned.
149 : template <typename T>
150 62 : std::string compare(const std::string& property, const std::set<T>& x, const std::set<T>& y) const
151 : {
152 62 : std::ostringstream out;
153 :
154 : // compute elements in x but not in y
155 62 : std::set<T> plus;
156 62 : std::set_difference(x.begin(), x.end(), y.begin(), y.end(), std::inserter(plus, plus.end()));
157 :
158 : // compute elements in y but not in x
159 62 : std::set<T> minus;
160 62 : std::set_difference(y.begin(), y.end(), x.begin(), x.end(), std::inserter(minus, minus.end()));
161 :
162 62 : if (!plus.empty() || !minus.empty())
163 : {
164 5 : out << "Difference in property " << property << " detected:";
165 14 : for (auto i = plus.begin(); i != plus.end(); ++i)
166 : {
167 9 : out << " +" << print(*i);
168 : }
169 16 : for (auto i = minus.begin(); i != minus.end(); ++i)
170 : {
171 11 : out << " -" << print(*i);
172 : }
173 5 : out << "\n";
174 5 : return out.str();
175 : }
176 57 : return "";
177 62 : }
178 :
179 : /// \brief Compares two values x and y of a given property. This function should
180 : /// be redefined in derived classes.
181 : /// \return An empty string if the two values are equal, otherwise a string indicating
182 : /// the differences between the two.
183 : std::string compare_property(const std::string& property, const std::string& /* x */, const std::string& /* y */) const
184 : {
185 : return "ERROR: unknown property " + property + " encountered!";
186 : }
187 :
188 : //--------------------------------------------//
189 : // miscellaneous functions
190 : //--------------------------------------------//
191 : /// \brief Returns the maximum length of the property names
192 9 : unsigned int max_key_length() const
193 : {
194 9 : unsigned int result = 0;
195 163 : for (auto i = m_data.begin(); i != m_data.end(); ++i)
196 : {
197 154 : result = (std::max)(static_cast< std::size_t >(result), i->first.size());
198 : }
199 9 : return result;
200 : }
201 :
202 154 : std::string align(const std::string& s, unsigned int n) const
203 : {
204 154 : if (s.size() >= n)
205 : {
206 16 : return s;
207 : }
208 276 : return s + std::string(n - s.size(), ' ');
209 : }
210 :
211 : /// \brief Collects the names of the elements of the container.
212 : /// The name of element x is retrieved by x.name().
213 : template <typename Container>
214 194 : std::set<core::identifier_string> names(const Container& v) const
215 : {
216 194 : std::set<core::identifier_string> result;
217 368 : for (auto i = v.begin(); i != v.end(); ++i)
218 : {
219 174 : result.insert(i->name());
220 : }
221 194 : return result;
222 0 : }
223 :
224 : /// \brief Default constructor for derived types
225 57 : data_property_map()
226 57 : {
227 57 : }
228 :
229 : /// \brief Initializes the property map with text containing lines in
230 : /// <tt>KEY = VALUE</tt> format.
231 51 : void parse_text(const std::string& text)
232 : {
233 130 : for (const std::string& line: utilities::split(text, "\n"))
234 : {
235 158 : std::vector<std::string> words = utilities::split(line, "=");
236 79 : if (words.size() == 2)
237 : {
238 77 : utilities::trim(words[0]);
239 77 : utilities::trim(words[1]);
240 77 : m_data[words[0]] = words[1];
241 : }
242 : }
243 51 : }
244 :
245 : public:
246 : /// The strings may appear in a random order, and not all of them need to be present
247 51 : data_property_map(const std::string& text)
248 51 : {
249 51 : parse_text(text);
250 51 : }
251 :
252 : /// \brief Returns a string representation of the properties
253 9 : std::string to_string() const
254 : {
255 9 : unsigned int n = max_key_length();
256 9 : std::vector<std::string> lines;
257 163 : for (auto i = m_data.begin(); i != m_data.end(); ++i)
258 : {
259 154 : lines.push_back(align(i->first, n) + " = " + i->second);
260 : }
261 18 : return utilities::string_join(lines, "\n");
262 9 : }
263 :
264 : /// \brief Returns the stored properties
265 1644 : const std::map<std::string, std::string>& data() const
266 : {
267 1644 : return m_data;
268 : }
269 :
270 : /// \brief Returns the value corresponding to key.
271 : /// Throws an exception if the key is not found.
272 0 : std::string operator[](const std::string& key) const
273 : {
274 0 : auto i = m_data.find(key);
275 0 : if (i == m_data.end())
276 : {
277 0 : throw mcrl2::runtime_error("property_map: could not find key " + key);
278 : }
279 0 : return i->second;
280 : }
281 :
282 : /// \brief Compares this property map with another property map.
283 : /// The function compare_property must be defined properly for all
284 : /// available properties.
285 : /// \return A string describing the differences found.
286 51 : std::string compare(const data_property_map& other) const
287 : {
288 51 : std::ostringstream out;
289 873 : for (auto i = m_data.begin(); i != m_data.end(); ++i)
290 : {
291 822 : auto j = other.data().find(i->first);
292 822 : if (j != other.data().end())
293 : {
294 77 : out << static_cast< Derived const& >(*this).compare_property(i->first, i->second, j->second);
295 : }
296 : }
297 102 : return out.str();
298 51 : }
299 : };
300 :
301 : template <typename PropertyMap>
302 26 : bool compare_property_maps(const std::string& message, const PropertyMap& map1, const std::string& expected_result)
303 : {
304 26 : PropertyMap map2(expected_result);
305 26 : std::string result = map1.compare(map2);
306 26 : if (!result.empty())
307 : {
308 0 : std::cerr << "------------------------------" << std::endl;
309 0 : std::cerr << " Failed test " << std::endl;
310 0 : std::cerr << "------------------------------" << std::endl;
311 0 : std::cerr << message << std::endl;
312 0 : std::cerr << "--- expected result ---" << std::endl;
313 0 : std::cerr << expected_result << std::endl;
314 0 : std::cerr << "--- found result ---" << std::endl;
315 0 : std::cerr << map1.to_string() << std::endl;
316 0 : std::cerr << "--- differences ---" << std::endl;
317 0 : std::cerr << result << std::endl;
318 : }
319 52 : return result.empty();
320 26 : }
321 :
322 : } // namespace detail
323 :
324 : } // namespace data
325 :
326 : } // namespace mcrl2
327 :
328 : #endif // MCRL2_DATA_DETAIL_DATA_PROPERTY_MAP_H
|