1 /*
2    Copyright (C) 2011 - 2018 by Sytyi Nick <nsytyi@gmail.com>
3    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
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 2 of the License, or
8    (at your option) any later version.
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY.
11 
12    See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * This file contains objects "tag" and "key", which are used to store
18  * information about tags and keys while annotation parsing.
19  */
20 
21 #pragma once
22 
23 #include <algorithm>
24 #include <iostream>
25 #include <map>
26 #include <sstream>
27 #include <string>
28 
29 class config;
30 
31 namespace schema_validation
32 {
33 /**
34  * class_key is used to save the information about one key.
35  * Key has next info: name, type, default value or key is mandatory.
36  */
37 class class_key
38 {
39 public:
class_key()40 	class_key()
41 		: name_("")
42 		, type_("")
43 		, default_("\"\"")
44 		, mandatory_(false)
45 	{
46 	}
47 
class_key(const std::string & name,const std::string & type,const std::string & def="\\"\\"")48 	class_key(const std::string& name, const std::string& type, const std::string& def = "\"\"")
49 		: name_(name)
50 		, type_(type)
51 		, default_(def)
52 		, mandatory_(def.empty())
53 	{
54 	}
55 
56 	class_key(const config&);
57 
get_name() const58 	const std::string& get_name() const
59 	{
60 		return name_;
61 	}
62 
get_type() const63 	const std::string& get_type() const
64 	{
65 		return type_;
66 	}
67 
get_default() const68 	const std::string& get_default() const
69 	{
70 		return default_;
71 	}
72 
is_mandatory() const73 	bool is_mandatory() const
74 	{
75 		return mandatory_;
76 	}
77 
set_name(const std::string & name)78 	void set_name(const std::string& name)
79 	{
80 		name_ = name;
81 	}
82 
set_type(const std::string & type)83 	void set_type(const std::string& type)
84 	{
85 		type_ = type;
86 	}
87 
set_default(const std::string & def)88 	void set_default(const std::string& def)
89 	{
90 		default_ = def;
91 		if(def.empty()) {
92 			mandatory_ = true;
93 		}
94 	}
95 
set_mandatory(bool mandatory)96 	void set_mandatory(bool mandatory)
97 	{
98 		mandatory_ = mandatory;
99 	}
100 
101 	/** is used to print key info
102 	 * the format is next
103 	 *  [key]
104 	 *      name="name"
105 	 *      type="type"
106 	 *      default="default"
107 	 *      mandatory="true/false"
108 	 *  [/key]
109 	 */
110 	void print(std::ostream& os, int level) const;
111 
112 	/** Compares keys by name. Used in std::sort, i.e. */
operator <(const class_key & k) const113 	bool operator<(const class_key& k) const
114 	{
115 		return (get_name() < k.get_name());
116 	}
117 
118 private:
119 	/** Name of key. */
120 	std::string name_;
121 
122 	/** Type of key. */
123 	std::string type_;
124 
125 	/** Default value. */
126 	std::string default_;
127 
128 	/** Shows, if key is a mandatory key. */
129 	bool mandatory_;
130 };
131 
132 /**
133  * Stores information about tag.
134  * Each tags is an element of great tag tree. This tree is close to filesystem:
135  * you can use links and special include directory global/
136  * Normally root is not mentioned in path.
137  * Each tag has name, minimum and maximum occasions number,
138  * and lists of subtags, keys and links.
139  */
140 class class_tag
141 {
142 public:
143 	using tag_map  = std::map<std::string, class_tag>;
144 	using key_map  = std::map<std::string, class_key>;
145 	using link_map = std::map<std::string, std::string>;
146 
class_tag()147 	class_tag()
148 		: name_("")
149 		, min_(0)
150 		, max_(0)
151 		, super_("")
152 		, tags_()
153 		, keys_()
154 		, links_()
155 	{
156 	}
157 
class_tag(const std::string & name,int min,int max,const std::string & super="")158 	class_tag(const std::string& name, int min, int max, const std::string& super = "")
159 		: name_(name)
160 		, min_(min)
161 		, max_(max)
162 		, super_(super)
163 		, tags_()
164 		, keys_()
165 		, links_()
166 	{
167 	}
168 
169 	class_tag(const config&);
170 
~class_tag()171 	~class_tag()
172 	{
173 	}
174 
175 	/** Prints information about tag to outputstream, recursively
176 	 * is used to print tag info
177 	 * the format is next
178 	 *  [tag]
179 	 *      subtags
180 	 *      keys
181 	 *      name="name"
182 	 *      min="min"
183 	 *      max="max"
184 	 *  [/tag]
185 	 */
186 	void print(std::ostream& os);
187 
get_name() const188 	const std::string& get_name() const
189 	{
190 		return name_;
191 	}
192 
get_min() const193 	int get_min() const
194 	{
195 		return min_;
196 	}
197 
get_max() const198 	int get_max() const
199 	{
200 		return max_;
201 	}
202 
get_super() const203 	const std::string& get_super() const
204 	{
205 		return super_;
206 	}
207 
is_extension() const208 	bool is_extension() const
209 	{
210 		return !super_.empty();
211 	}
212 
set_name(const std::string & name)213 	void set_name(const std::string& name)
214 	{
215 		name_ = name;
216 	}
217 
set_min(int o)218 	void set_min(int o)
219 	{
220 		min_ = o;
221 	}
222 
set_max(int o)223 	void set_max(int o)
224 	{
225 		max_ = o;
226 	}
227 
set_min(const std::string & s)228 	void set_min(const std::string& s)
229 	{
230 		std::istringstream i(s);
231 		if(!(i >> min_)) {
232 			min_ = 0;
233 		}
234 	}
235 
set_max(const std::string & s)236 	void set_max(const std::string& s)
237 	{
238 		std::istringstream i(s);
239 		if(!(i >> max_)) {
240 			max_ = 0;
241 		}
242 	}
243 
set_super(const std::string & s)244 	void set_super(const std::string& s)
245 	{
246 		super_ = s;
247 	}
248 
add_key(const class_key & new_key)249 	void add_key(const class_key& new_key)
250 	{
251 		keys_.emplace(new_key.get_name(), new_key);
252 	}
253 
add_tag(const class_tag & new_tag)254 	void add_tag(const class_tag& new_tag)
255 	{
256 		tags_.emplace(new_tag.name_, new_tag);
257 	}
258 
259 	void add_link(const std::string& link);
260 
261 	/**
262 	 * Tags are usually organized in tree.
263 	 * This function helps to add a tag to his exact place in tree
264 	 * @param path - path in subtree to exact place of tag
265 	 * @param tag  - tag to add
266 	 * @param root - root of schema tree - use to support of adding to link.
267 	 * Path is getting shotter and shoter with each call.
268 	 * Path should look like tag1/tag2/parent/ Slash at end is mandatory.
269 	 */
270 	void add_tag(const std::string& path, const class_tag& tag, class_tag& root);
271 
operator <(const class_tag & t) const272 	bool operator<(const class_tag& t) const
273 	{
274 		return name_ < t.name_;
275 	}
276 
operator ==(const class_tag & other) const277 	bool operator==(const class_tag& other) const
278 	{
279 		return name_ == other.name_;
280 	}
281 
282 	/** Returns pointer to child key. */
283 	const class_key* find_key(const std::string& name) const;
284 
285 	/** Returns pointer to child link. */
286 	const std::string* find_link(const std::string& name) const;
287 
288 	/**
289 	 * Returns pointer to tag using full path to it.
290 	 * Also work with links
291 	 */
292 	const class_tag* find_tag(const std::string& fullpath, const class_tag& root) const;
293 
294 	/** Calls the expansion on each child. */
295 	void expand_all(class_tag& root);
296 
tags() const297 	const tag_map& tags() const
298 	{
299 		return tags_;
300 	}
301 
keys() const302 	const key_map& keys() const
303 	{
304 		return keys_;
305 	}
306 
links() const307 	const link_map& links() const
308 	{
309 		return links_;
310 	}
311 
remove_key_by_name(const std::string & name)312 	void remove_key_by_name(const std::string& name)
313 	{
314 		keys_.erase(name);
315 	}
316 
317 	/** Removes all keys with this type. Works recursively */
318 	void remove_keys_by_type(const std::string& type);
319 
320 #ifdef _MSC_VER
321 	// MSVC throws an error if this method is private.
322 	// so, define it as public to prevent that. The error is:
323 	// error C2248: 'schema_validation::class_tag::find_tag' : cannot
324 	// access private member declared in class 'schema_validation::class_tag'
325 
326 	class_tag* find_tag(const std::string& fullpath, class_tag& root);
327 #endif
328 
329 	// class_tag & operator= (const class_tag&);
330 
331 private:
332 	/** name of tag. */
333 	std::string name_;
334 
335 	/** number of minimum occasions. */
336 	int min_;
337 
338 	/** number of maximum occasions. */
339 	int max_;
340 
341 	/**
342 	 * name of tag to extend "super-tag"
343 	 * Extension is smth like inheritance and is used in case
344 	 * when you need to use another tag with all his
345 	 * keys, children, etc. But you also want to allow extra subtags of that tags,
346 	 * so just linking that tag wouldn't help at all.
347 	 */
348 	std::string super_;
349 
350 	/** children tags*/
351 	tag_map tags_;
352 
353 	/** keys*/
354 	key_map keys_;
355 
356 	/** links to possible children. */
357 	link_map links_;
358 
359 	/**
360 	 * the same as class_tag::print(std::ostream&)
361 	 * but indents different levels with step space.
362 	 * @param os stream to print
363 	 * @param level  current level of indentation
364 	 * @param step   step to next indent
365 	 */
366 	void printl(std::ostream& os, int level, int step = 4);
367 
368 #ifndef _MSC_VER
369 	// this is complementary with the above #ifdef for the MSVC error
370 	class_tag* find_tag(const std::string& fullpath, class_tag& root);
371 #endif
372 
add_tags(const tag_map & list)373 	void add_tags(const tag_map& list)
374 	{
375 		tags_.insert(list.begin(), list.end());
376 	}
377 
add_keys(const key_map & list)378 	void add_keys(const key_map& list)
379 	{
380 		keys_.insert(list.begin(), list.end());
381 	}
382 
add_links(const link_map & list)383 	void add_links(const link_map& list)
384 	{
385 		links_.insert(list.begin(), list.end());
386 	}
387 
388 	/** Copies tags, keys and links of tag to this. */
389 	void append_super(const class_tag& tag, const std::string& super);
390 
391 	/** Expands all "super" copying their data to this. */
392 	void expand(class_tag& root);
393 };
394 }
395