PageRenderTime 72ms CodeModel.GetById 24ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 27#include "linden_common.h"
 28
 29#include "llrngwriter.h"
 30#include "lluicolor.h"
 31#include "lluictrlfactory.h"
 32
 33static 	LLInitParam::Parser::parser_read_func_map_t sReadFuncs;
 34static 	LLInitParam::Parser::parser_write_func_map_t sWriteFuncs;
 35static 	LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs;
 36
 37//
 38// LLRNGWriter - writes Relax NG schema files based on a param block
 39//
 40LLRNGWriter::LLRNGWriter()
 41: Parser(sReadFuncs, sWriteFuncs, sInspectFuncs)
 42{
 43	// register various callbacks for inspecting the contents of a param block
 44	registerInspectFunc<bool>(boost::bind(&LLRNGWriter::writeAttribute, this, "boolean", _1, _2, _3, _4));
 45	registerInspectFunc<std::string>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
 46	registerInspectFunc<U8>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedByte", _1, _2, _3, _4));
 47	registerInspectFunc<S8>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedByte", _1, _2, _3, _4));
 48	registerInspectFunc<U16>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedShort", _1, _2, _3, _4));
 49	registerInspectFunc<S16>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedShort", _1, _2, _3, _4));
 50	registerInspectFunc<U32>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedInt", _1, _2, _3, _4));
 51	registerInspectFunc<S32>(boost::bind(&LLRNGWriter::writeAttribute, this, "integer", _1, _2, _3, _4));
 52	registerInspectFunc<F32>(boost::bind(&LLRNGWriter::writeAttribute, this, "float", _1, _2, _3, _4));
 53	registerInspectFunc<F64>(boost::bind(&LLRNGWriter::writeAttribute, this, "double", _1, _2, _3, _4));
 54	registerInspectFunc<LLColor4>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
 55	registerInspectFunc<LLUIColor>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
 56	registerInspectFunc<LLUUID>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
 57	registerInspectFunc<LLSD>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
 58}
 59
 60void LLRNGWriter::writeRNG(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
 61{
 62	mGrammarNode = node;
 63	mGrammarNode->setName("grammar");
 64	mGrammarNode->createChild("xmlns", true)->setStringValue("http://relaxng.org/ns/structure/1.0");
 65	mGrammarNode->createChild("datatypeLibrary", true)->setStringValue("http://www.w3.org/2001/XMLSchema-datatypes");
 66	mGrammarNode->createChild("ns", true)->setStringValue(xml_namespace);
 67
 68	node = mGrammarNode->createChild("start", false);
 69	node = node->createChild("ref", false);
 70	node->createChild("name", true)->setStringValue(type_name);
 71
 72	addDefinition(type_name, block);
 73}
 74
 75void LLRNGWriter::addDefinition(const std::string& type_name, const LLInitParam::BaseBlock& block)
 76{
 77	if (mDefinedElements.find(type_name) != mDefinedElements.end()) return;
 78	mDefinedElements.insert(type_name);
 79
 80	LLXMLNodePtr node = mGrammarNode->createChild("define", false);
 81	node->createChild("name", true)->setStringValue(type_name);
 82
 83	mElementNode = node->createChild("element", false);
 84	mElementNode->createChild("name", true)->setStringValue(type_name);
 85	mChildrenNode = mElementNode->createChild("zeroOrMore", false)->createChild("choice", false);
 86
 87	mAttributesWritten.first = mElementNode;
 88	mAttributesWritten.second.clear();
 89	mElementsWritten.clear();
 90
 91	block.inspectBlock(*this);
 92
 93	// add includes for all possible children
 94	const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
 95	const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
 96	
 97	// add include declarations for all valid children
 98	for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
 99		it != widget_registryp->currentRegistrar().endItems();
100		++it)
101	{
102		std::string child_name = it->first;
103		if (child_name == type_name)
104		{
105			continue;
106		}
107		
108		LLXMLNodePtr old_element_node = mElementNode;
109		LLXMLNodePtr old_child_node = mChildrenNode;
110		//FIXME: add LLDefaultParamBlockRegistry back when working on schema generation
111		//addDefinition(child_name, (*LLDefaultParamBlockRegistry::instance().getValue(type))());
112		mElementNode = old_element_node;
113		mChildrenNode = old_child_node;
114
115		mChildrenNode->createChild("ref", false)->createChild("name", true)->setStringValue(child_name);
116	}
117
118	if (mChildrenNode->mChildren.isNull())
119	{
120		// remove unused children node
121		mChildrenNode->mParent->mParent->deleteChild(mChildrenNode->mParent);
122	}
123}
124
125void 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)
126{
127	if (max_count == 0) return;
128
129	name_stack_t non_empty_names;
130	std::string attribute_name;
131	for (name_stack_t::const_iterator it = stack.begin();
132		it != stack.end();
133		++it)
134	{
135		const std::string& name = it->first;
136		if (!name.empty())
137		{
138			non_empty_names.push_back(*it);
139		}
140	}
141
142	if (non_empty_names.empty()) return;
143
144	for (name_stack_t::const_iterator it = non_empty_names.begin();
145		it != non_empty_names.end();
146		++it)
147	{
148		if (!attribute_name.empty())
149		{
150			attribute_name += ".";
151		}
152		attribute_name += it->first;
153	}
154
155	// singular attribute, e.g. <foo bar="1"/>
156	if (non_empty_names.size() == 1 && max_count == 1)
157	{
158		if (mAttributesWritten.second.find(attribute_name) == mAttributesWritten.second.end())
159		{
160			LLXMLNodePtr node = createCardinalityNode(mElementNode, min_count, max_count)->createChild("attribute", false);
161			node->createChild("name", true)->setStringValue(attribute_name);
162			node->createChild("data", false)->createChild("type", true)->setStringValue(type);
163
164			mAttributesWritten.second.insert(attribute_name);
165		}
166	}
167	// compound attribute
168	else
169	{
170		std::string element_name;
171
172		// traverse all but last element, leaving that as an attribute name
173		name_stack_t::const_iterator end_it = non_empty_names.end();
174		end_it--;
175
176		for (name_stack_t::const_iterator it = non_empty_names.begin();
177			it != end_it;
178			++it)
179		{
180			if (it != non_empty_names.begin())
181			{
182				element_name += ".";
183			}
184			element_name += it->first;
185		}
186
187		elements_map_t::iterator found_it = mElementsWritten.find(element_name);
188		// <choice>
189		//   <group>
190		//     <optional>
191		//	     <attribute name="foo.bar"><data type="string"/></attribute>
192		//     </optional>
193		//     <optional>
194		//       <attribute name="foo.baz"><data type="integer"/></attribute>
195		//     </optional>
196		//   </group>
197		//   <element name="foo">
198		//     <optional>
199		//       <attribute name="bar"><data type="string"/></attribute>
200		//     </optional>
201		//     <optional>
202		//       <attribute name="baz"><data type="string"/></attribute>
203		//     </optional>
204		//   </element>
205		//   <element name="outer.foo">
206		//     <ref name="foo"/>
207		//   </element>
208		// </choice>
209
210		if (found_it != mElementsWritten.end())
211		{
212			// reuse existing element
213			LLXMLNodePtr choice_node = found_it->second.first;
214
215			// attribute with this name not already written?
216			if (found_it->second.second.find(attribute_name) == found_it->second.second.end())
217			{
218				// append to <group>
219				LLXMLNodePtr node = choice_node->mChildren->head;
220				node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
221				node->createChild("name", true)->setStringValue(attribute_name);
222				addTypeNode(node, type, possible_values);
223
224				// append to <element>
225				node = choice_node->mChildren->head->mNext->mChildren->head;
226				node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
227				node->createChild("name", true)->setStringValue(non_empty_names.back().first);
228				addTypeNode(node, type, possible_values);
229
230				// append to <element>
231				//node = choice_node->mChildren->head->mNext->mNext->mChildren->head;
232				//node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
233				//node->createChild("name", true)->setStringValue(non_empty_names.back().first);
234				//addTypeNode(node, type, possible_values);
235
236				found_it->second.second.insert(attribute_name);
237			}
238		}
239		else
240		{
241			LLXMLNodePtr choice_node = mElementNode->createChild("choice", false);
242
243			LLXMLNodePtr node = choice_node->createChild("group", false);
244			node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
245			node->createChild("name", true)->setStringValue(attribute_name);
246			addTypeNode(node, type, possible_values);
247
248			node = choice_node->createChild("optional", false);
249			node = node->createChild("element", false);
250			node->createChild("name", true)->setStringValue(element_name);
251			node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
252			node->createChild("name", true)->setStringValue(non_empty_names.back().first);
253			addTypeNode(node, type, possible_values);
254			
255			//node = choice_node->createChild("optional", false);
256			//node = node->createChild("element", false);
257			//node->createChild("name", true)->setStringValue(mDefinitionName + "." + element_name);
258			//node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
259			//node->createChild("name", true)->setStringValue(non_empty_names.back().first);
260			//addTypeNode(node, type, possible_values);
261
262			attribute_data_t& attribute_data = mElementsWritten[element_name];
263			attribute_data.first = choice_node;
264			attribute_data.second.insert(attribute_name);
265		}
266	}
267}
268
269void LLRNGWriter::addTypeNode(LLXMLNodePtr parent_node, const std::string& type, const std::vector<std::string>* possible_values)
270{
271	if (possible_values)
272	{
273		LLXMLNodePtr enum_node = parent_node->createChild("choice", false);
274		for (std::vector<std::string>::const_iterator it = possible_values->begin();
275			it != possible_values->end();
276			++it)
277		{
278			enum_node->createChild("value", false)->setStringValue(*it);
279		}
280	}
281	else
282	{
283		parent_node->createChild("data", false)->createChild("type", true)->setStringValue(type);
284	}
285}
286
287LLXMLNodePtr LLRNGWriter::createCardinalityNode(LLXMLNodePtr parent_node, S32 min_count, S32 max_count)
288{
289	// unlinked by default, meaning this attribute is forbidden
290	LLXMLNodePtr count_node = new LLXMLNode();
291	if (min_count == 0)
292	{
293		if (max_count == 1)
294		{
295			count_node = parent_node->createChild("optional", false);
296		}
297		else if (max_count > 1)
298		{
299			count_node = parent_node->createChild("zeroOrMore", false);
300		}	
301	}
302	else if (min_count >= 1)
303	{
304		if (max_count == 1 && min_count == 1)
305		{
306			// just add raw element, will count as 1 and only 1
307			count_node = parent_node;
308		}
309		else
310		{
311			count_node = parent_node->createChild("oneOrMore", false);
312		}
313	}
314	return count_node;
315}