Ember
Loading...
Searching...
No Matches
ParserConfig.cpp
Go to the documentation of this file.
2#include "Utils/Logger.h"
3#include <algorithm>
4
5namespace EmberCore {
6
8 // Initialize with default values (already set in struct definitions)
9
10 // Initialize default blackboard type mappings
11 blackboard_config_.type_mappings["bool"] = "BOOL";
12 blackboard_config_.type_mappings["int"] = "INT";
13 blackboard_config_.type_mappings["float"] = "FLOAT";
14 blackboard_config_.type_mappings["double"] = "DOUBLE";
15 blackboard_config_.type_mappings["string"] = "STRING";
16 blackboard_config_.type_mappings["vector"] = "VECTOR";
17 blackboard_config_.type_mappings["set"] = "SET";
18 blackboard_config_.type_mappings["map"] = "MAP";
19 blackboard_config_.type_mappings["auto"] = "AUTO";
20}
21
22nlohmann::json ParserConfig::ToJson() const {
23 nlohmann::json json;
24
25 // Document config
26 json["document"] = {{"root_element", document_config_.root_element},
27 {"main_tree_attribute", document_config_.main_tree_attribute},
28 {"case_sensitive", document_config_.case_sensitive},
29 {"allow_unknown_elements", document_config_.allow_unknown_elements},
30 {"whitespace_handling", WhitespaceHandlingToJson(document_config_.whitespace_handling)},
31 {"naming_convention", NamingConventionToJson(document_config_.naming_convention)}};
32
33 // Tree config
34 json["tree"] = {{"behavior_tree_element", tree_config_.behavior_tree_element},
35 {"tree_id_attribute", tree_config_.tree_id_attribute},
36 {"allow_multiple_trees", tree_config_.allow_multiple_trees},
37 {"subtree_element", tree_config_.subtree_element},
38 {"subtree_reference_attribute", tree_config_.subtree_reference_attribute},
39 {"validate_tree_structure", tree_config_.validate_tree_structure},
40 {"require_root_node", tree_config_.require_root_node}};
41
42 // Node config
43 json["node"] = {{"control_element", node_config_.control_element},
44 {"action_element", node_config_.action_element},
45 {"condition_element", node_config_.condition_element},
46 {"decorator_element", node_config_.decorator_element},
47 {"generic_node_element", node_config_.generic_node_element},
48 {"node_id_attribute", node_config_.node_id_attribute},
49 {"node_name_attribute", node_config_.node_name_attribute},
50 {"node_type_attribute", node_config_.node_type_attribute},
51 {"allow_custom_attributes", node_config_.allow_custom_attributes}};
52
53 // Classification config
54 json["classification"] = {{"strategy", ClassificationStrategyToJson(classification_config_.strategy)},
55 {"control_types", classification_config_.control_types},
56 {"decorator_types", classification_config_.decorator_types},
57 {"action_types", classification_config_.action_types},
58 {"condition_types", classification_config_.condition_types},
59 {"unknown_behavior", UnknownTypeBehaviorToJson(classification_config_.unknown_behavior)},
60 {"type_attribute_name", classification_config_.type_attribute_name}};
61
62 // Blackboard config
63 json["blackboard"] = {{"blackboard_element", blackboard_config_.blackboard_element},
64 {"blackboard_id_attribute", blackboard_config_.blackboard_id_attribute},
65 {"parent_attribute", blackboard_config_.parent_attribute},
66 {"entry_element", blackboard_config_.entry_element},
67 {"entry_key_attribute", blackboard_config_.entry_key_attribute},
68 {"entry_type_attribute", blackboard_config_.entry_type_attribute},
69 {"entry_value_attribute", blackboard_config_.entry_value_attribute},
70 {"type_mappings", blackboard_config_.type_mappings},
71 {"allow_undefined_keys", blackboard_config_.allow_undefined_keys}};
72
73 // Advanced config
74 json["advanced"] = {{"enable_progress_reporting", advanced_config_.enable_progress_reporting},
75 {"detailed_error_messages", advanced_config_.detailed_error_messages},
76 {"track_line_column", advanced_config_.track_line_column},
77 {"subtree_expansion", SubTreeExpansionToJson(advanced_config_.subtree_expansion)},
78 {"max_recursion_depth", advanced_config_.max_recursion_depth},
79 {"xml_namespace_uri", advanced_config_.xml_namespace_uri},
80 {"preserve_comments", advanced_config_.preserve_comments},
81 {"preserve_attribute_order", advanced_config_.preserve_attribute_order},
82 {"custom_validation_rules", advanced_config_.custom_validation_rules}};
83
84 return json;
85}
86
87void ParserConfig::FromJson(const nlohmann::json &json) {
88 try {
89 // Document config
90 if (json.contains("document")) {
91 const auto &doc = json["document"];
92 if (doc.contains("root_element"))
93 document_config_.root_element = doc["root_element"];
94 if (doc.contains("main_tree_attribute"))
95 document_config_.main_tree_attribute = doc["main_tree_attribute"];
96 if (doc.contains("case_sensitive"))
97 document_config_.case_sensitive = doc["case_sensitive"];
98 if (doc.contains("allow_unknown_elements"))
99 document_config_.allow_unknown_elements = doc["allow_unknown_elements"];
100 if (doc.contains("whitespace_handling")) {
101 document_config_.whitespace_handling = WhitespaceHandlingFromJson(doc["whitespace_handling"]);
102 }
103 if (doc.contains("naming_convention")) {
104 document_config_.naming_convention = NamingConventionFromJson(doc["naming_convention"]);
105 }
106 }
107
108 // Tree config
109 if (json.contains("tree")) {
110 const auto &tree = json["tree"];
111 if (tree.contains("behavior_tree_element"))
112 tree_config_.behavior_tree_element = tree["behavior_tree_element"];
113 if (tree.contains("tree_id_attribute"))
114 tree_config_.tree_id_attribute = tree["tree_id_attribute"];
115 if (tree.contains("allow_multiple_trees"))
116 tree_config_.allow_multiple_trees = tree["allow_multiple_trees"];
117 if (tree.contains("subtree_element"))
118 tree_config_.subtree_element = tree["subtree_element"];
119 if (tree.contains("subtree_reference_attribute"))
120 tree_config_.subtree_reference_attribute = tree["subtree_reference_attribute"];
121 if (tree.contains("validate_tree_structure"))
122 tree_config_.validate_tree_structure = tree["validate_tree_structure"];
123 if (tree.contains("require_root_node"))
124 tree_config_.require_root_node = tree["require_root_node"];
125 }
126
127 // Node config
128 if (json.contains("node")) {
129 const auto &node = json["node"];
130 if (node.contains("control_element"))
131 node_config_.control_element = node["control_element"];
132 if (node.contains("action_element"))
133 node_config_.action_element = node["action_element"];
134 if (node.contains("condition_element"))
135 node_config_.condition_element = node["condition_element"];
136 if (node.contains("decorator_element"))
137 node_config_.decorator_element = node["decorator_element"];
138 if (node.contains("generic_node_element"))
139 node_config_.generic_node_element = node["generic_node_element"];
140 if (node.contains("node_id_attribute"))
141 node_config_.node_id_attribute = node["node_id_attribute"];
142 if (node.contains("node_name_attribute"))
143 node_config_.node_name_attribute = node["node_name_attribute"];
144 if (node.contains("node_type_attribute"))
145 node_config_.node_type_attribute = node["node_type_attribute"];
146 if (node.contains("allow_custom_attributes"))
147 node_config_.allow_custom_attributes = node["allow_custom_attributes"];
148 }
149
150 // Classification config
151 if (json.contains("classification")) {
152 const auto &classification = json["classification"];
153 if (classification.contains("strategy")) {
154 classification_config_.strategy = ClassificationStrategyFromJson(classification["strategy"]);
155 }
156 if (classification.contains("control_types")) {
157 classification_config_.control_types = classification["control_types"].get<std::vector<String>>();
158 }
159 if (classification.contains("decorator_types")) {
160 classification_config_.decorator_types = classification["decorator_types"].get<std::vector<String>>();
161 }
162 if (classification.contains("action_types")) {
163 classification_config_.action_types = classification["action_types"].get<std::vector<String>>();
164 }
165 if (classification.contains("condition_types")) {
166 classification_config_.condition_types = classification["condition_types"].get<std::vector<String>>();
167 }
168 if (classification.contains("unknown_behavior")) {
169 classification_config_.unknown_behavior =
170 UnknownTypeBehaviorFromJson(classification["unknown_behavior"]);
171 }
172 if (classification.contains("type_attribute_name")) {
173 classification_config_.type_attribute_name = classification["type_attribute_name"];
174 }
175 }
176
177 // Blackboard config
178 if (json.contains("blackboard")) {
179 const auto &blackboard = json["blackboard"];
180 if (blackboard.contains("blackboard_element"))
181 blackboard_config_.blackboard_element = blackboard["blackboard_element"];
182 if (blackboard.contains("blackboard_id_attribute"))
183 blackboard_config_.blackboard_id_attribute = blackboard["blackboard_id_attribute"];
184 if (blackboard.contains("parent_attribute"))
185 blackboard_config_.parent_attribute = blackboard["parent_attribute"];
186 if (blackboard.contains("entry_element"))
187 blackboard_config_.entry_element = blackboard["entry_element"];
188 if (blackboard.contains("entry_key_attribute"))
189 blackboard_config_.entry_key_attribute = blackboard["entry_key_attribute"];
190 if (blackboard.contains("entry_type_attribute"))
191 blackboard_config_.entry_type_attribute = blackboard["entry_type_attribute"];
192 if (blackboard.contains("entry_value_attribute"))
193 blackboard_config_.entry_value_attribute = blackboard["entry_value_attribute"];
194 if (blackboard.contains("type_mappings")) {
195 blackboard_config_.type_mappings = blackboard["type_mappings"].get<std::map<String, String>>();
196 }
197 if (blackboard.contains("allow_undefined_keys"))
198 blackboard_config_.allow_undefined_keys = blackboard["allow_undefined_keys"];
199 }
200
201 // Advanced config
202 if (json.contains("advanced")) {
203 const auto &advanced = json["advanced"];
204 if (advanced.contains("enable_progress_reporting"))
205 advanced_config_.enable_progress_reporting = advanced["enable_progress_reporting"];
206 if (advanced.contains("detailed_error_messages"))
207 advanced_config_.detailed_error_messages = advanced["detailed_error_messages"];
208 if (advanced.contains("track_line_column"))
209 advanced_config_.track_line_column = advanced["track_line_column"];
210 if (advanced.contains("subtree_expansion")) {
211 advanced_config_.subtree_expansion = SubTreeExpansionFromJson(advanced["subtree_expansion"]);
212 }
213 if (advanced.contains("max_recursion_depth"))
214 advanced_config_.max_recursion_depth = advanced["max_recursion_depth"];
215 if (advanced.contains("xml_namespace_uri"))
216 advanced_config_.xml_namespace_uri = advanced["xml_namespace_uri"];
217 if (advanced.contains("preserve_comments"))
218 advanced_config_.preserve_comments = advanced["preserve_comments"];
219 if (advanced.contains("preserve_attribute_order"))
220 advanced_config_.preserve_attribute_order = advanced["preserve_attribute_order"];
221 if (advanced.contains("custom_validation_rules"))
222 advanced_config_.custom_validation_rules = advanced["custom_validation_rules"];
223 }
224 } catch (const std::exception &e) {
225 LOG_ERROR("ParserConfig", "Error parsing JSON configuration: " + std::string(e.what()));
226 throw;
227 }
228}
229
230bool ParserConfig::Validate(std::vector<String> &errors) const {
231 errors.clear();
232
233 // Validate document config
234 if (document_config_.root_element.empty()) {
235 errors.push_back("Document root element cannot be empty");
236 }
237
238 // Validate tree config
239 if (tree_config_.behavior_tree_element.empty()) {
240 errors.push_back("Behavior tree element cannot be empty");
241 }
242 if (tree_config_.tree_id_attribute.empty()) {
243 errors.push_back("Tree ID attribute cannot be empty");
244 }
245
246 // Validate node config
247 if (node_config_.control_element.empty()) {
248 errors.push_back("Control element cannot be empty");
249 }
250 if (node_config_.action_element.empty()) {
251 errors.push_back("Action element cannot be empty");
252 }
253 if (node_config_.condition_element.empty()) {
254 errors.push_back("Condition element cannot be empty");
255 }
256 if (node_config_.decorator_element.empty()) {
257 errors.push_back("Decorator element cannot be empty");
258 }
259 if (node_config_.node_id_attribute.empty()) {
260 errors.push_back("Node ID attribute cannot be empty");
261 }
262
263 // Validate classification config
264 if (classification_config_.control_types.empty()) {
265 errors.push_back("Control types list cannot be empty");
266 }
267 if (classification_config_.decorator_types.empty()) {
268 errors.push_back("Decorator types list cannot be empty");
269 }
270
271 // Validate blackboard config
272 if (blackboard_config_.blackboard_element.empty()) {
273 errors.push_back("Blackboard element cannot be empty");
274 }
275 if (blackboard_config_.entry_element.empty()) {
276 errors.push_back("Blackboard entry element cannot be empty");
277 }
278
279 // Validate advanced config
280 if (advanced_config_.max_recursion_depth <= 0) {
281 errors.push_back("Maximum recursion depth must be positive");
282 }
283
284 return errors.empty();
285}
286
287bool ParserConfig::IsControlType(const String &type) const {
288 return std::find(classification_config_.control_types.begin(), classification_config_.control_types.end(), type) !=
289 classification_config_.control_types.end();
290}
291
292bool ParserConfig::IsDecoratorType(const String &type) const {
293 return std::find(classification_config_.decorator_types.begin(), classification_config_.decorator_types.end(),
294 type) != classification_config_.decorator_types.end();
295}
296
297bool ParserConfig::IsActionType(const String &type) const {
298 if (classification_config_.action_types.empty()) {
299 // If no specific action types defined, any non-control/decorator/condition is an action
300 return !IsControlType(type) && !IsDecoratorType(type) && !IsConditionType(type);
301 }
302 return std::find(classification_config_.action_types.begin(), classification_config_.action_types.end(), type) !=
303 classification_config_.action_types.end();
304}
305
306bool ParserConfig::IsConditionType(const String &type) const {
307 if (classification_config_.condition_types.empty()) {
308 return false; // No conditions defined
309 }
310 return std::find(classification_config_.condition_types.begin(), classification_config_.condition_types.end(),
311 type) != classification_config_.condition_types.end();
312}
313
315 ParserConfig config;
316 // Default values are already set in the constructor and struct defaults
317 return config;
318}
319
321 ParserConfig config;
322
323 // Make it more permissive for generic use
327
328 return config;
329}
330
331// Helper methods for enum conversion
332
334 switch (strategy) {
336 return "by_element_name";
338 return "by_attribute";
340 return "hybrid";
341 default:
342 return "by_element_name";
343 }
344}
345
347 String str = json.get<String>();
348 if (str == "by_element_name")
350 if (str == "by_attribute")
352 if (str == "hybrid")
355}
356
358 switch (behavior) {
360 return "error";
362 return "warning";
364 return "generic_node";
365 default:
366 return "error";
367 }
368}
369
371 String str = json.get<String>();
372 if (str == "error")
374 if (str == "warning")
376 if (str == "generic_node")
379}
380
382 switch (handling) {
384 return "preserve";
386 return "trim";
388 return "normalize";
389 default:
390 return "trim";
391 }
392}
393
395 String str = json.get<String>();
396 if (str == "preserve")
398 if (str == "trim")
400 if (str == "normalize")
403}
404
406 switch (expansion) {
408 return "immediate";
410 return "lazy";
412 return "manual";
413 default:
414 return "immediate";
415 }
416}
417
419 String str = json.get<String>();
420 if (str == "immediate")
422 if (str == "lazy")
424 if (str == "manual")
427}
428
430 switch (convention) {
432 return "strict";
434 return "balanced";
436 return "loose";
437 default:
438 return "balanced";
439 }
440}
441
443 String str = json.get<String>();
444 if (str == "strict")
446 if (str == "balanced")
448 if (str == "loose")
451}
452
453} // namespace EmberCore
#define LOG_ERROR(category, message)
Definition Logger.h:116
BlackboardConfig blackboard_config_
bool Validate(std::vector< String > &errors) const
SubTreeExpansion
SubTree expansion strategy.
static nlohmann::json SubTreeExpansionToJson(SubTreeExpansion expansion)
AdvancedConfig advanced_config_
static WhitespaceHandling WhitespaceHandlingFromJson(const nlohmann::json &json)
NamingConvention
Naming convention strictness.
ClassificationStrategy
Strategy for classifying node types.
static ParserConfig CreateDefault()
static nlohmann::json UnknownTypeBehaviorToJson(UnknownTypeBehavior behavior)
bool IsConditionType(const String &type) const
static NamingConvention NamingConventionFromJson(const nlohmann::json &json)
UnknownTypeBehavior
How to handle unknown/unmapped types.
static nlohmann::json NamingConventionToJson(NamingConvention convention)
void FromJson(const nlohmann::json &json)
static ParserConfig CreateGeneric()
bool IsDecoratorType(const String &type) const
WhitespaceHandling
Whitespace handling strategy.
static SubTreeExpansion SubTreeExpansionFromJson(const nlohmann::json &json)
static nlohmann::json ClassificationStrategyToJson(ClassificationStrategy strategy)
bool IsActionType(const String &type) const
nlohmann::json ToJson() const
static ClassificationStrategy ClassificationStrategyFromJson(const nlohmann::json &json)
DocumentConfig document_config_
bool IsControlType(const String &type) const
static nlohmann::json WhitespaceHandlingToJson(WhitespaceHandling handling)
ClassificationConfig classification_config_
static UnknownTypeBehavior UnknownTypeBehaviorFromJson(const nlohmann::json &json)
Main types header for EmberCore.
std::string String
Framework-agnostic string type.
Definition String.h:14