/indra/llui/llrngwriter.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 315 lines · 203 code · 37 blank · 75 comment · 45 complexity · 7d6d307678ceb52de2e941f55fab18a0 MD5 · raw file

  1. /**
  2. * @file llrngwriter.cpp
  3. * @brief Generates Relax NG schema from param blocks
  4. *
  5. * $LicenseInfo:firstyear=2003&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "llrngwriter.h"
  28. #include "lluicolor.h"
  29. #include "lluictrlfactory.h"
  30. static LLInitParam::Parser::parser_read_func_map_t sReadFuncs;
  31. static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs;
  32. static LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs;
  33. //
  34. // LLRNGWriter - writes Relax NG schema files based on a param block
  35. //
  36. LLRNGWriter::LLRNGWriter()
  37. : Parser(sReadFuncs, sWriteFuncs, sInspectFuncs)
  38. {
  39. // register various callbacks for inspecting the contents of a param block
  40. registerInspectFunc<bool>(boost::bind(&LLRNGWriter::writeAttribute, this, "boolean", _1, _2, _3, _4));
  41. registerInspectFunc<std::string>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
  42. registerInspectFunc<U8>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedByte", _1, _2, _3, _4));
  43. registerInspectFunc<S8>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedByte", _1, _2, _3, _4));
  44. registerInspectFunc<U16>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedShort", _1, _2, _3, _4));
  45. registerInspectFunc<S16>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedShort", _1, _2, _3, _4));
  46. registerInspectFunc<U32>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedInt", _1, _2, _3, _4));
  47. registerInspectFunc<S32>(boost::bind(&LLRNGWriter::writeAttribute, this, "integer", _1, _2, _3, _4));
  48. registerInspectFunc<F32>(boost::bind(&LLRNGWriter::writeAttribute, this, "float", _1, _2, _3, _4));
  49. registerInspectFunc<F64>(boost::bind(&LLRNGWriter::writeAttribute, this, "double", _1, _2, _3, _4));
  50. registerInspectFunc<LLColor4>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
  51. registerInspectFunc<LLUIColor>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
  52. registerInspectFunc<LLUUID>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
  53. registerInspectFunc<LLSD>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
  54. }
  55. void LLRNGWriter::writeRNG(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
  56. {
  57. mGrammarNode = node;
  58. mGrammarNode->setName("grammar");
  59. mGrammarNode->createChild("xmlns", true)->setStringValue("http://relaxng.org/ns/structure/1.0");
  60. mGrammarNode->createChild("datatypeLibrary", true)->setStringValue("http://www.w3.org/2001/XMLSchema-datatypes");
  61. mGrammarNode->createChild("ns", true)->setStringValue(xml_namespace);
  62. node = mGrammarNode->createChild("start", false);
  63. node = node->createChild("ref", false);
  64. node->createChild("name", true)->setStringValue(type_name);
  65. addDefinition(type_name, block);
  66. }
  67. void LLRNGWriter::addDefinition(const std::string& type_name, const LLInitParam::BaseBlock& block)
  68. {
  69. if (mDefinedElements.find(type_name) != mDefinedElements.end()) return;
  70. mDefinedElements.insert(type_name);
  71. LLXMLNodePtr node = mGrammarNode->createChild("define", false);
  72. node->createChild("name", true)->setStringValue(type_name);
  73. mElementNode = node->createChild("element", false);
  74. mElementNode->createChild("name", true)->setStringValue(type_name);
  75. mChildrenNode = mElementNode->createChild("zeroOrMore", false)->createChild("choice", false);
  76. mAttributesWritten.first = mElementNode;
  77. mAttributesWritten.second.clear();
  78. mElementsWritten.clear();
  79. block.inspectBlock(*this);
  80. // add includes for all possible children
  81. const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
  82. const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
  83. // add include declarations for all valid children
  84. for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
  85. it != widget_registryp->currentRegistrar().endItems();
  86. ++it)
  87. {
  88. std::string child_name = it->first;
  89. if (child_name == type_name)
  90. {
  91. continue;
  92. }
  93. LLXMLNodePtr old_element_node = mElementNode;
  94. LLXMLNodePtr old_child_node = mChildrenNode;
  95. //FIXME: add LLDefaultParamBlockRegistry back when working on schema generation
  96. //addDefinition(child_name, (*LLDefaultParamBlockRegistry::instance().getValue(type))());
  97. mElementNode = old_element_node;
  98. mChildrenNode = old_child_node;
  99. mChildrenNode->createChild("ref", false)->createChild("name", true)->setStringValue(child_name);
  100. }
  101. if (mChildrenNode->mChildren.isNull())
  102. {
  103. // remove unused children node
  104. mChildrenNode->mParent->mParent->deleteChild(mChildrenNode->mParent);
  105. }
  106. }
  107. void LLRNGWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
  108. {
  109. if (max_count == 0) return;
  110. name_stack_t non_empty_names;
  111. std::string attribute_name;
  112. for (name_stack_t::const_iterator it = stack.begin();
  113. it != stack.end();
  114. ++it)
  115. {
  116. const std::string& name = it->first;
  117. if (!name.empty())
  118. {
  119. non_empty_names.push_back(*it);
  120. }
  121. }
  122. if (non_empty_names.empty()) return;
  123. for (name_stack_t::const_iterator it = non_empty_names.begin();
  124. it != non_empty_names.end();
  125. ++it)
  126. {
  127. if (!attribute_name.empty())
  128. {
  129. attribute_name += ".";
  130. }
  131. attribute_name += it->first;
  132. }
  133. // singular attribute, e.g. <foo bar="1"/>
  134. if (non_empty_names.size() == 1 && max_count == 1)
  135. {
  136. if (mAttributesWritten.second.find(attribute_name) == mAttributesWritten.second.end())
  137. {
  138. LLXMLNodePtr node = createCardinalityNode(mElementNode, min_count, max_count)->createChild("attribute", false);
  139. node->createChild("name", true)->setStringValue(attribute_name);
  140. node->createChild("data", false)->createChild("type", true)->setStringValue(type);
  141. mAttributesWritten.second.insert(attribute_name);
  142. }
  143. }
  144. // compound attribute
  145. else
  146. {
  147. std::string element_name;
  148. // traverse all but last element, leaving that as an attribute name
  149. name_stack_t::const_iterator end_it = non_empty_names.end();
  150. end_it--;
  151. for (name_stack_t::const_iterator it = non_empty_names.begin();
  152. it != end_it;
  153. ++it)
  154. {
  155. if (it != non_empty_names.begin())
  156. {
  157. element_name += ".";
  158. }
  159. element_name += it->first;
  160. }
  161. elements_map_t::iterator found_it = mElementsWritten.find(element_name);
  162. // <choice>
  163. // <group>
  164. // <optional>
  165. // <attribute name="foo.bar"><data type="string"/></attribute>
  166. // </optional>
  167. // <optional>
  168. // <attribute name="foo.baz"><data type="integer"/></attribute>
  169. // </optional>
  170. // </group>
  171. // <element name="foo">
  172. // <optional>
  173. // <attribute name="bar"><data type="string"/></attribute>
  174. // </optional>
  175. // <optional>
  176. // <attribute name="baz"><data type="string"/></attribute>
  177. // </optional>
  178. // </element>
  179. // <element name="outer.foo">
  180. // <ref name="foo"/>
  181. // </element>
  182. // </choice>
  183. if (found_it != mElementsWritten.end())
  184. {
  185. // reuse existing element
  186. LLXMLNodePtr choice_node = found_it->second.first;
  187. // attribute with this name not already written?
  188. if (found_it->second.second.find(attribute_name) == found_it->second.second.end())
  189. {
  190. // append to <group>
  191. LLXMLNodePtr node = choice_node->mChildren->head;
  192. node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
  193. node->createChild("name", true)->setStringValue(attribute_name);
  194. addTypeNode(node, type, possible_values);
  195. // append to <element>
  196. node = choice_node->mChildren->head->mNext->mChildren->head;
  197. node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
  198. node->createChild("name", true)->setStringValue(non_empty_names.back().first);
  199. addTypeNode(node, type, possible_values);
  200. // append to <element>
  201. //node = choice_node->mChildren->head->mNext->mNext->mChildren->head;
  202. //node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
  203. //node->createChild("name", true)->setStringValue(non_empty_names.back().first);
  204. //addTypeNode(node, type, possible_values);
  205. found_it->second.second.insert(attribute_name);
  206. }
  207. }
  208. else
  209. {
  210. LLXMLNodePtr choice_node = mElementNode->createChild("choice", false);
  211. LLXMLNodePtr node = choice_node->createChild("group", false);
  212. node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
  213. node->createChild("name", true)->setStringValue(attribute_name);
  214. addTypeNode(node, type, possible_values);
  215. node = choice_node->createChild("optional", false);
  216. node = node->createChild("element", false);
  217. node->createChild("name", true)->setStringValue(element_name);
  218. node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
  219. node->createChild("name", true)->setStringValue(non_empty_names.back().first);
  220. addTypeNode(node, type, possible_values);
  221. //node = choice_node->createChild("optional", false);
  222. //node = node->createChild("element", false);
  223. //node->createChild("name", true)->setStringValue(mDefinitionName + "." + element_name);
  224. //node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
  225. //node->createChild("name", true)->setStringValue(non_empty_names.back().first);
  226. //addTypeNode(node, type, possible_values);
  227. attribute_data_t& attribute_data = mElementsWritten[element_name];
  228. attribute_data.first = choice_node;
  229. attribute_data.second.insert(attribute_name);
  230. }
  231. }
  232. }
  233. void LLRNGWriter::addTypeNode(LLXMLNodePtr parent_node, const std::string& type, const std::vector<std::string>* possible_values)
  234. {
  235. if (possible_values)
  236. {
  237. LLXMLNodePtr enum_node = parent_node->createChild("choice", false);
  238. for (std::vector<std::string>::const_iterator it = possible_values->begin();
  239. it != possible_values->end();
  240. ++it)
  241. {
  242. enum_node->createChild("value", false)->setStringValue(*it);
  243. }
  244. }
  245. else
  246. {
  247. parent_node->createChild("data", false)->createChild("type", true)->setStringValue(type);
  248. }
  249. }
  250. LLXMLNodePtr LLRNGWriter::createCardinalityNode(LLXMLNodePtr parent_node, S32 min_count, S32 max_count)
  251. {
  252. // unlinked by default, meaning this attribute is forbidden
  253. LLXMLNodePtr count_node = new LLXMLNode();
  254. if (min_count == 0)
  255. {
  256. if (max_count == 1)
  257. {
  258. count_node = parent_node->createChild("optional", false);
  259. }
  260. else if (max_count > 1)
  261. {
  262. count_node = parent_node->createChild("zeroOrMore", false);
  263. }
  264. }
  265. else if (min_count >= 1)
  266. {
  267. if (max_count == 1 && min_count == 1)
  268. {
  269. // just add raw element, will count as 1 and only 1
  270. count_node = parent_node;
  271. }
  272. else
  273. {
  274. count_node = parent_node->createChild("oneOrMore", false);
  275. }
  276. }
  277. return count_node;
  278. }