data_def.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. #include "data_def.h"
  2. #include <sstream>
  3. #include <boost/foreach.hpp>
  4. // todo: add more context to thrown DataDefinitionErrors?
  5. using namespace ISC::Data;
  6. // todo: is there a direct way to get a std::string from an enum label?
  7. static std::string
  8. get_type_string(Element::types type)
  9. {
  10. switch(type) {
  11. case Element::integer:
  12. return std::string("integer");
  13. case Element::real:
  14. return std::string("real");
  15. case Element::boolean:
  16. return std::string("boolean");
  17. case Element::string:
  18. return std::string("string");
  19. case Element::list:
  20. return std::string("list");
  21. case Element::map:
  22. return std::string("map");
  23. default:
  24. return std::string("unknown");
  25. }
  26. }
  27. static Element::types
  28. get_type_value(const std::string& type_name) {
  29. if (type_name == "integer") {
  30. return Element::integer;
  31. } else if (type_name == "real") {
  32. return Element::real;
  33. } else if (type_name == "boolean") {
  34. return Element::boolean;
  35. } else if (type_name == "string") {
  36. return Element::string;
  37. } else if (type_name == "list") {
  38. return Element::list;
  39. } else if (type_name == "map") {
  40. return Element::map;
  41. } else {
  42. throw DataDefinitionError(type_name + " is not a valid type name");
  43. }
  44. }
  45. static void
  46. check_leaf_item(const ElementPtr& spec, const std::string& name, Element::types type, bool mandatory)
  47. {
  48. if (spec->contains(name)) {
  49. if (spec->get(name)->get_type() == type) {
  50. return;
  51. } else {
  52. throw DataDefinitionError(name + " not of type " + get_type_string(type));
  53. }
  54. } else if (mandatory) {
  55. // todo: want parent item name, and perhaps some info about location
  56. // in list? or just catch and throw new...
  57. // or make this part non-throwing and check return value...
  58. throw DataDefinitionError(name + " missing in " + spec->str());
  59. }
  60. }
  61. static void check_config_item_list(const ElementPtr& spec);
  62. static void
  63. check_config_item(const ElementPtr& spec) {
  64. check_leaf_item(spec, "item_name", Element::string, true);
  65. check_leaf_item(spec, "item_type", Element::string, true);
  66. check_leaf_item(spec, "item_optional", Element::boolean, true);
  67. check_leaf_item(spec, "item_default",
  68. get_type_value(spec->get("item_type")->string_value()),
  69. !spec->get("item_optional")->bool_value()
  70. );
  71. // if list, check the list definition
  72. if (get_type_value(spec->get("item_type")->string_value()) == Element::list) {
  73. check_leaf_item(spec, "list_item_spec", Element::map, true);
  74. check_config_item(spec->get("list_item_spec"));
  75. }
  76. // todo: add stuff for type map
  77. if (get_type_value(spec->get("item_type")->string_value()) == Element::map) {
  78. check_leaf_item(spec, "map_item_spec", Element::list, true);
  79. check_config_item_list(spec);
  80. }
  81. }
  82. static void
  83. check_config_item_list(const ElementPtr& spec) {
  84. if (spec->get_type() != Element::list) {
  85. throw DataDefinitionError("config_data is not a list of elements");
  86. }
  87. BOOST_FOREACH(ElementPtr item, spec->list_value()) {
  88. check_config_item(item);
  89. }
  90. }
  91. static void
  92. check_command(const ElementPtr& spec) {
  93. check_leaf_item(spec, "command_name", Element::string, true);
  94. check_leaf_item(spec, "command_args", Element::list, true);
  95. check_config_item_list(spec->get("command_args"));
  96. }
  97. static void
  98. check_command_list(const ElementPtr& spec) {
  99. if (spec->get_type() != Element::list) {
  100. throw DataDefinitionError("commands is not a list of elements");
  101. }
  102. BOOST_FOREACH(ElementPtr item, spec->list_value()) {
  103. check_command(item);
  104. }
  105. }
  106. static void
  107. check_data_specification(const ElementPtr& spec) {
  108. check_leaf_item(spec, "module_name", Element::string, true);
  109. // not mandatory; module could just define commands and have
  110. // no config
  111. if (spec->contains("config_data")) {
  112. check_config_item_list(spec->get("config_data"));
  113. }
  114. if (spec->contains("commands")) {
  115. check_command_list(spec->get("commands"));
  116. }
  117. }
  118. // checks whether the given element is a valid data definition
  119. // throws a DataDefinitionError if the specification is bad
  120. static void
  121. check_definition(const ElementPtr& def)
  122. {
  123. if (!def->contains("data_specification")) {
  124. throw DataDefinitionError("Data specification does not contain data_specification element");
  125. } else {
  126. check_data_specification(def->get("data_specification"));
  127. }
  128. }
  129. DataDefinition::DataDefinition(std::istream& in, const bool check)
  130. throw(ParseError, DataDefinitionError) {
  131. definition = Element::create_from_string(in);
  132. // make sure the whole structure is complete and valid
  133. if (check) {
  134. check_definition(definition);
  135. }
  136. }
  137. //
  138. // helper functions for validation
  139. //
  140. static bool
  141. check_type(ElementPtr spec, ElementPtr element)
  142. {
  143. std::string cur_item_type;
  144. cur_item_type = spec->get("item_type")->string_value();
  145. if (cur_item_type == "any") {
  146. return true;
  147. }
  148. switch (element->get_type()) {
  149. case Element::integer:
  150. return cur_item_type == "integer";
  151. break;
  152. case Element::real:
  153. return cur_item_type == "real";
  154. break;
  155. case Element::boolean:
  156. return cur_item_type == "boolean";
  157. break;
  158. case Element::string:
  159. return cur_item_type == "string";
  160. break;
  161. case Element::list:
  162. return cur_item_type == "list";
  163. break;
  164. case Element::map:
  165. return cur_item_type == "map";
  166. break;
  167. }
  168. return false;
  169. }
  170. bool
  171. DataDefinition::validate_item(const ElementPtr spec, const ElementPtr data) {
  172. std::cout << "Validating type of " << data << std::endl;
  173. if (!check_type(spec, data)) {
  174. std::cout << "type mismatch; not " << spec->get("item_type") << ": " << data << std::endl;
  175. std::cout << spec << std::endl;
  176. return false;
  177. }
  178. if (data->get_type() == Element::list) {
  179. BOOST_FOREACH(ElementPtr list_el, data->list_value()) {
  180. if (!validate_spec(spec->get("list_item_spec"), list_el)) {
  181. return false;
  182. }
  183. }
  184. }
  185. if (data->get_type() == Element::map) {
  186. if (!validate_spec_list(spec->get("map_item_spec"), data)) {
  187. return false;
  188. }
  189. }
  190. return true;
  191. }
  192. // spec is a map with item_name etc, data is a map
  193. bool
  194. DataDefinition::validate_spec(const ElementPtr spec, const ElementPtr data) {
  195. std::string item_name = spec->get("item_name")->string_value();
  196. bool optional = spec->get("item_optional")->bool_value();
  197. ElementPtr data_el;
  198. std::cout << "check for item with name " << item_name << std::endl;
  199. data_el = data->get(item_name);
  200. if (data_el) {
  201. if (!validate_item(spec, data_el)) {
  202. return false;
  203. }
  204. } else {
  205. if (!optional) {
  206. std::cout << "non-optional value not found" << std::endl;
  207. return false;
  208. }
  209. }
  210. return true;
  211. }
  212. // spec is a list of maps, data is a map
  213. bool
  214. DataDefinition::validate_spec_list(const ElementPtr spec, const ElementPtr data) {
  215. ElementPtr cur_data_el;
  216. std::string cur_item_name;
  217. BOOST_FOREACH(ElementPtr cur_spec_el, spec->list_value()) {
  218. if (!validate_spec(cur_spec_el, data)) {
  219. return false;
  220. }
  221. }
  222. return true;
  223. }
  224. // TODO
  225. // this function does *not* check if the specification is in correct
  226. // form, we should do that in the constructor
  227. bool
  228. DataDefinition::validate(const ElementPtr data) {
  229. ElementPtr spec = definition->find("data_specification/config_data");
  230. return validate_spec_list(spec, data);
  231. }