Example #1
0
void JSONTest::testStringElement()
{
	std::string json = "[ \"value\" ]";
	Parser parser;
	Var result;

	try
	{
		DefaultHandler handler;
		parser.setHandler(&handler);
		parser.parse(json);
		result = handler.result();
	}
	catch(JSONException& jsone)
	{
		std::cout << jsone.message() << std::endl;
		assert(false);
	}

	assert(result.type() == typeid(Array::Ptr));

	Array::Ptr array = result.extract<Array::Ptr>();
	Var test = array->get(0);
	assert(test.isString());
	std::string value = test.convert<std::string>();
	assert(value.compare("value") == 0);
}
Example #2
0
bool Value::ToBool(void) const
{
	switch (GetType()) {
		case ValueNumber:
			return static_cast<bool>(boost::get<double>(m_Value));

		case ValueBoolean:
			return boost::get<bool>(m_Value);

		case ValueString:
			return !boost::get<String>(m_Value).IsEmpty();

		case ValueObject:
			if (IsObjectType<Dictionary>()) {
				Dictionary::Ptr dictionary = *this;
				return dictionary->GetLength() > 0;
			} else if (IsObjectType<Array>()) {
				Array::Ptr array = *this;
				return array->GetLength() > 0;
			} else {
				return true;
			}

		case ValueEmpty:
			return false;

		default:
			BOOST_THROW_EXCEPTION(std::runtime_error("Invalid variant type."));
	}
}
Example #3
0
bool HostGroup::ResolveGroupMembership(const Host::Ptr& host, bool add, int rstack) {

	if (add && rstack > 20) {
		Log(LogWarning, "HostGroup")
			<< "Too many nested groups for group '" << GetName() << "': Host '"
			<< host->GetName() << "' membership assignment failed.";

		return false;
	}

	Array::Ptr groups = GetGroups();

	if (groups && groups->GetLength() > 0) {
		ObjectLock olock(groups);

		for (const String& name : groups) {
			HostGroup::Ptr group = HostGroup::GetByName(name);

			if (group && !group->ResolveGroupMembership(host, add, rstack + 1))
				return false;
		}
	}

	if (add)
		AddMember(host);
	else
		RemoveMember(host);

	return true;
}
Example #4
0
void TimePeriod::PurgeSegments(double end)
{
	ASSERT(OwnsLock());

	Log(LogDebug, "TimePeriod")
	    << "Purging segments older than '" << Utility::FormatDateTime("%c", end)
	    << "' from TimePeriod '" << GetName() << "'";

	if (GetValidBegin().IsEmpty() || end < GetValidBegin())
		return;

	SetValidBegin(end);

	Array::Ptr segments = GetSegments();

	if (!segments)
		return;

	Array::Ptr newSegments = new Array();

	/* Remove old segments. */
	ObjectLock dlock(segments);
	for (const Dictionary::Ptr& segment : segments) {
		if (segment->Get("end") >= end)
			newSegments->Add(segment);
	}

	SetSegments(newSegments);
}
Example #5
0
void JSONTest::testNullElement()
{
	std::string json = "[ null ]";
	Parser parser;
	Var result;

	try
	{
		DefaultHandler handler;
		parser.setHandler(&handler);
		parser.parse(json);
		result = handler.result();
	}
	catch(JSONException& jsone)
	{
		std::cout << jsone.message() << std::endl;
		assert(false);
	}

	assert(result.type() == typeid(Array::Ptr));

	Array::Ptr array = result.extract<Array::Ptr>();
	assert(array->isNull(0));
	Var test = array->get(0);
	assert(test.isEmpty());
}
Example #6
0
void JSONTest::testEmptyObjectElement()
{
	std::string json = "[{}]";
	Parser parser;
	Var result;

	try
	{
		DefaultHandler handler;
		parser.setHandler(&handler);
		parser.parse(json);
		result = handler.result();
	}
	catch(JSONException& jsone)
	{
		std::cout << jsone.message() << std::endl;
		assert(false);
	}

	assert(result.type() == typeid(Array::Ptr));

	Array::Ptr array = result.extract<Array::Ptr>();
	Object::Ptr object = array->getObject(0);
	assert(object->size() == 0);
}
Example #7
0
Array::Ptr ScriptUtils::Range(const std::vector<Value>& arguments)
{
	double start, end, increment;

	switch (arguments.size()) {
		case 1:
			start = 0;
			end = arguments[0];
			increment = 1;
			break;
		case 2:
			start = arguments[0];
			end = arguments[1];
			increment = 1;
			break;
		case 3:
			start = arguments[0];
			end = arguments[1];
			increment = arguments[2];
			break;
		default:
			BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid number of arguments for range()"));
	}

	Array::Ptr result = new Array();

	if ((start < end && increment <= 0) ||
	    (start > end && increment >= 0))
		return result;

	for (double i = start; (increment > 0 ? i < end : i > end); i += increment)
		result->Add(i);

	return result;
}
Example #8
0
/**
 * Executes the auto completion script via HTTP and returns HTTP and user errors.
 *
 * @param session Local session handler.
 * @param command The auto completion string.
 * @param sandboxed Whether to run this sandboxed.
 * @return Result value, also contains user errors.
 */
Array::Ptr ConsoleCommand::AutoCompleteScript(const String& session, const String& command, bool sandboxed)
{
	/* Extend the url parameters for the request. */
	l_Url->SetPath({ "v1", "console", "auto-complete-script" });

	l_Url->SetQuery({
		{"session",   session},
		{"command",   command},
		{"sandboxed", sandboxed ? "1" : "0"}
	});

	Dictionary::Ptr jsonResponse = SendRequest();

	/* Extract the result, and handle user input errors too. */
	Array::Ptr results = jsonResponse->Get("results");
	Array::Ptr suggestions;

	if (results && results->GetLength() > 0) {
		Dictionary::Ptr resultInfo = results->Get(0);

		if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299) {
			suggestions = resultInfo->Get("suggestions");
		} else {
			String errorMessage = resultInfo->Get("status");
			BOOST_THROW_EXCEPTION(ScriptError(errorMessage));
		}
	}

	return suggestions;
}
Example #9
0
void JSONTest::testDoubleElement()
{
	std::string json = "[ 123.45 ]";
	Parser parser;
	Var result;

	try
	{
		DefaultHandler handler;
		parser.setHandler(&handler);
		parser.parse(json);
		result = handler.result();
	}
	catch(JSONException& jsone)
	{
		std::cout << jsone.message() << std::endl;
		assert(false);
	}

	assert(result.type() == typeid(Array::Ptr));

	Array::Ptr array = result.extract<Array::Ptr>();
	Var test = array->get(0);
	assert(test.isNumeric());
	double value = test;
	assert(value == 123.45);
}
Example #10
0
char *ConsoleCommand::ConsoleCompleteHelper(const char *word, int state)
{
	static std::vector<String> matches;

	if (state == 0) {
		if (!l_Url)
			matches = ConsoleHandler::GetAutocompletionSuggestions(word, *l_ScriptFrame);
		else {
			Array::Ptr suggestions;

			/* Remote debug console. */
			try {
				suggestions = AutoCompleteScript(l_Session, word, l_ScriptFrame->Sandboxed);
			} catch (...) {
				return nullptr; //Errors are just ignored here.
			}

			matches.clear();

			ObjectLock olock(suggestions);
			std::copy(suggestions->Begin(), suggestions->End(), std::back_inserter(matches));
		}
	}

	if (state >= static_cast<int>(matches.size()))
		return nullptr;

	return strdup(matches[state].CStr());
}
Example #11
0
void ParseHandler::startArray()
{
	Array::Ptr newArr = new Array();

	if ( _stack.empty() ) // The first array
	{
		_result = newArr;
	}
	else
	{
		Var parent = _stack.top();

		if ( parent.type() == typeid(Array::Ptr) )
		{
			Array::Ptr arr = parent.extract<Array::Ptr>();
			arr->add(newArr);
		}
		else if ( parent.type() == typeid(Object::Ptr) )
		{
			poco_assert_dbg(!_key.empty());
			Object::Ptr obj = parent.extract<Object::Ptr>();
			obj->set(_key, newArr);
			_key.clear();
		}
	}

	_stack.push(newArr);
}
Example #12
0
Value ApiEvents::CheckResultAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
	if (!origin.FromClient->GetEndpoint())
		return Empty;

	if (!params)
		return Empty;

	CheckResult::Ptr cr = make_shared<CheckResult>();

	Dictionary::Ptr vcr = params->Get("cr");
	Array::Ptr vperf = vcr->Get("performance_data");
	vcr->Remove("performance_data");

	Deserialize(cr, params->Get("cr"), true);

	Array::Ptr rperf = make_shared<Array>();

	ObjectLock olock(vperf);
	BOOST_FOREACH(const Value& vp, vperf) {
		Value p;

		if (vp.IsObjectType<Dictionary>()) {
			PerfdataValue::Ptr val = make_shared<PerfdataValue>();
			Deserialize(val, vp, true);
			rperf->Add(val);
		} else
			rperf->Add(vp);
	}
Example #13
0
bool ConsoleHandler::AutocompleteScriptHelper(HttpRequest& request, HttpResponse& response,
    const String& command, const String& session, bool sandboxed)
{
	Log(LogInformation, "Console")
	    << "Auto-completing expression: " << command;

	ApiScriptFrame& lsf = l_ApiScriptFrames[session];
	lsf.Seen = Utility::GetTime();

	if (!lsf.Locals)
		lsf.Locals = new Dictionary();

	Array::Ptr results = new Array();
	Dictionary::Ptr resultInfo = new Dictionary();

	ScriptFrame frame;
	frame.Locals = lsf.Locals;
	frame.Self = lsf.Locals;
	frame.Sandboxed = sandboxed;

	resultInfo->Set("code", 200);
	resultInfo->Set("status", "Auto-completed successfully.");
	resultInfo->Set("suggestions", Array::FromVector(GetAutocompletionSuggestions(command, frame)));

	results->Add(resultInfo);

	Dictionary::Ptr result = new Dictionary();
	result->Set("results", results);

	response.SetStatus(200, "OK");
	HttpUtility::SendJsonBody(response, result);

	return true;
}
Example #14
0
ConfigItem::Ptr ConfigItemBuilder::Compile(void)
{
	if (m_Type.IsEmpty()) {
		std::ostringstream msgbuf;
		msgbuf << "The type name of an object may not be empty: " << m_DebugInfo;
		BOOST_THROW_EXCEPTION(std::invalid_argument(msgbuf.str()));
	}

	if (!DynamicType::GetByName(m_Type)) {
		std::ostringstream msgbuf;
		msgbuf << "The type '" + m_Type + "' is unknown: " << m_DebugInfo;
		BOOST_THROW_EXCEPTION(std::invalid_argument(msgbuf.str()));
	}

	std::vector<Expression *> exprs;

	Array::Ptr templateArray = new Array();
	templateArray->Add(m_Name);

	exprs.push_back(new SetExpression(MakeIndexer(ScopeThis, "templates"), OpSetAdd,
	    new LiteralExpression(templateArray), m_DebugInfo));

	DictExpression *dexpr = new DictExpression(m_Expressions, m_DebugInfo);
	dexpr->MakeInline();
	exprs.push_back(dexpr);

	boost::shared_ptr<DictExpression> exprl = boost::make_shared<DictExpression>(exprs, m_DebugInfo);
	exprl->MakeInline();

	return new ConfigItem(m_Type, m_Name, m_Abstract, exprl, m_Filter,
	    m_DebugInfo, m_Scope, m_Zone);
}
Example #15
0
bool ServiceGroup::EvaluateObjectRule(const Service::Ptr& service, const ConfigItem::Ptr& group)
{
	String group_name = group->GetName();

	CONTEXT("Evaluating rule for group '" + group_name + "'");

	Host::Ptr host = service->GetHost();

	ScriptFrame frame;
	if (group->GetScope())
		group->GetScope()->CopyTo(frame.Locals);
	frame.Locals->Set("host", host);
	frame.Locals->Set("service", service);

	if (!group->GetFilter()->Evaluate(frame).GetValue().ToBool())
		return false;

	Log(LogDebug, "ServiceGroup")
	    << "Assigning membership for group '" << group_name << "' to service '" << service->GetName() << "'";

	Array::Ptr groups = service->GetGroups();
	groups->Add(group_name);

	return true;
}
Example #16
0
Value CommandsTable::CustomVariableNamesAccessor(const Value& row)
{
	Command::Ptr command = static_cast<Command::Ptr>(row);

	if (!command)
		return Empty;

	Dictionary::Ptr vars;

	{
		ObjectLock olock(command);
		vars = CompatUtility::GetCustomAttributeConfig(command);
	}

	if (!vars)
		return Empty;

	Array::Ptr cv = new Array();

	String key;
	Value value;

	ObjectLock xlock(vars);
	BOOST_FOREACH(tie(key, value), vars) {
		cv->Add(key);
	}
Example #17
0
	static inline ExpressionResult For(ScriptFrame& frame, const String& fkvar, const String& fvvar, const Value& value, Expression *expression, const DebugInfo& debugInfo = DebugInfo())
	{
		if (value.IsObjectType<Array>()) {
			if (!fvvar.IsEmpty())
				BOOST_THROW_EXCEPTION(ScriptError("Cannot use dictionary iterator for array.", debugInfo));

			Array::Ptr arr = value;

			for (Array::SizeType i = 0; i < arr->GetLength(); i++) {
				frame.Locals->Set(fkvar, arr->Get(i));
				ExpressionResult res = expression->Evaluate(frame);
				CHECK_RESULT_LOOP(res);
			}
		} else if (value.IsObjectType<Dictionary>()) {
			if (fvvar.IsEmpty())
				BOOST_THROW_EXCEPTION(ScriptError("Cannot use array iterator for dictionary.", debugInfo));

			Dictionary::Ptr dict = value;
			std::vector<String> keys;

			{
				ObjectLock olock(dict);
				BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
					keys.push_back(kv.first);
				}
			}

			BOOST_FOREACH(const String& key, keys) {
				frame.Locals->Set(fkvar, key);
				frame.Locals->Set(fvvar, dict->Get(key));
				ExpressionResult res = expression->Evaluate(frame);
				CHECK_RESULT_LOOP(res);
			}
Example #18
0
void TimePeriod::PurgeSegments(double end)
{
	ASSERT(OwnsLock());

	Log(LogDebug, "icinga", "Purging segments older than '" + Utility::FormatDateTime("%c", end) + "' from TimePeriod '" + GetName() + "'");

	if (GetValidBegin().IsEmpty() || end < GetValidBegin())
		return;

	SetValidBegin(end);

	Array::Ptr segments = GetSegments();

	if (!segments)
		return;

	Array::Ptr newSegments = make_shared<Array>();

	/* Remove old segments. */
	ObjectLock dlock(segments);
	BOOST_FOREACH(const Dictionary::Ptr& segment, segments) {
		if (segment->Get("end") >= end)
			newSegments->Add(segment);
	}

	SetSegments(newSegments);
}
Var Query::find(const std::string& path) const
{
	Var result = _source;
	StringTokenizer tokenizer(path, ".");
	for(StringTokenizer::Iterator token = tokenizer.begin(); token != tokenizer.end(); token++)
	{
		if ( !result.isEmpty() )
		{
			std::vector<int> indexes;
			RegularExpression::MatchVec matches;
			int firstOffset = -1;
			int offset = 0;
			RegularExpression regex("\\[([0-9]+)\\]");
			while(regex.match(*token, offset, matches) > 0 )
			{
				if ( firstOffset == -1 )
				{
					firstOffset = static_cast<int>(matches[0].offset);
				}
				std::string num = token->substr(matches[1].offset, matches[1].length);
				indexes.push_back(NumberParser::parse(num));
				offset = static_cast<int>(matches[0].offset + matches[0].length);
			}

			std::string name(*token);
			if ( firstOffset != -1 )
			{
				name = name.substr(0, firstOffset);
			}

			if ( name.length() > 0 )
			{
				if ( result.type() == typeid(Object::Ptr) )
				{
					Object::Ptr o = result.extract<Object::Ptr>();
					result = o->get(name);
				}
			}

			if (    !result.isEmpty()
			        && !indexes.empty() )
			{
				for(std::vector<int>::iterator it = indexes.begin(); it != indexes.end(); ++it )
				{
					if ( result.type() == typeid(Array::Ptr) )
					{
						Array::Ptr array = result.extract<Array::Ptr>();
						result = array->get(*it);
						if ( result.isEmpty() )
						{
							break;
						}
					}
				}
			}
		}
	}
	return result;
}
Example #20
0
/**
 * Makes a deep clone of an array
 * and its elements.
 * 
 * @returns a copy of the array.
 */
Object::Ptr Array::Clone(void) const
{
	Array::Ptr arr = new Array();
	
	ObjectLock olock(this);
	BOOST_FOREACH(const Value& val, m_Data) {
		arr->Add(val.Clone());
	}
Example #21
0
static Array::Ptr DictionaryKeys(void)
{
	ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
	Dictionary::Ptr self = static_cast<Dictionary::Ptr>(vframe->Self);
	Array::Ptr keys = new Array();
	ObjectLock olock(self);
	BOOST_FOREACH(const Dictionary::Pair& kv, self) {
		keys->Add(kv.first);
	}
bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors)
{
	std::vector<Object::Ptr> parents = DependencyGraph::GetParents(object);

	Type::Ptr type = object->GetReflectionType();

	if (!parents.empty() && !cascade) {
		if (errors)
			errors->Add("Object '" + object->GetName() + "' of type '" + type->GetName() +
			    "' cannot be deleted because other objects depend on it. "
			    "Use cascading delete to delete it anyway.");

		return false;
	}

	for (const Object::Ptr& pobj : parents) {
		ConfigObject::Ptr parentObj = dynamic_pointer_cast<ConfigObject>(pobj);

		if (!parentObj)
			continue;

		DeleteObjectHelper(parentObj, cascade, errors);
	}

	ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, object->GetName());

	try {
		/* mark this object for cluster delete event */
		object->SetExtension("ConfigObjectDeleted", true);
		/* triggers signal for DB IDO and other interfaces */
		object->Deactivate(true);

		if (item)
			item->Unregister();
		else
			object->Unregister();

	} catch (const std::exception& ex) {
		if (errors)
			errors->Add(DiagnosticInformation(ex));

		return false;
	}

	String path = GetObjectConfigPath(object->GetReflectionType(), object->GetName());

	if (Utility::PathExists(path)) {
		if (unlink(path.CStr()) < 0 && errno != ENOENT) {
			BOOST_THROW_EXCEPTION(posix_error()
			    << boost::errinfo_api_function("unlink")
			    << boost::errinfo_errno(errno)
			    << boost::errinfo_file_name(path));
		}
	}

	return true;
}
Example #23
0
static Array::Ptr SerializeArray(const Array::Ptr& input, int attributeTypes)
{
	Array::Ptr result = new Array();

	ObjectLock olock(input);

	BOOST_FOREACH(const Value& value, input) {
		result->Add(Serialize(value, attributeTypes));
	}
Example #24
0
bool Dependency::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule)
{
	DebugInfo di = rule.GetDebugInfo();

	std::ostringstream msgbuf;
	msgbuf << "Evaluating 'apply' rule (" << di << ")";
	CONTEXT(msgbuf.str());

	Host::Ptr host;
	Service::Ptr service;
	tie(host, service) = GetHostService(checkable);

	ScriptFrame frame;
	if (rule.GetScope())
		rule.GetScope()->CopyTo(frame.Locals);
	frame.Locals->Set("host", host);
	if (service)
		frame.Locals->Set("service", service);

	Value vinstances;

	if (rule.GetFTerm()) {
		try {
			vinstances = rule.GetFTerm()->Evaluate(frame);
		} catch (const std::exception&) {
			/* Silently ignore errors here and assume there are no instances. */
			return false;
		}
	} else {
		Array::Ptr instances = new Array();
		instances->Add("");
		vinstances = instances;
	}

	bool match = false;

	if (vinstances.IsObjectType<Array>()) {
		if (!rule.GetFVVar().IsEmpty())
			BOOST_THROW_EXCEPTION(ScriptError("Array iterator requires value to be an array.", di));

		Array::Ptr arr = vinstances;
		Array::Ptr arrclone = arr->ShallowClone();

		ObjectLock olock(arrclone);
		BOOST_FOREACH(const Value& instance, arrclone) {
			String name = rule.GetName();

			if (!rule.GetFKVar().IsEmpty()) {
				frame.Locals->Set(rule.GetFKVar(), instance);
				name += instance;
			}

			if (EvaluateApplyRuleInstance(checkable, name, frame, rule))
				match = true;
		}
	} else if (vinstances.IsObjectType<Dictionary>()) {
Example #25
0
void TimePeriod::AddSegment(double begin, double end)
{
	ASSERT(OwnsLock());

	Log(LogDebug, "TimePeriod")
	    << "Adding segment '" << Utility::FormatDateTime("%c", begin) << "' <-> '"
	    << Utility::FormatDateTime("%c", end) << "' to TimePeriod '" << GetName() << "'";

	if (GetValidBegin().IsEmpty() || begin < GetValidBegin())
		SetValidBegin(begin);

	if (GetValidEnd().IsEmpty() || end > GetValidEnd())
		SetValidEnd(end);

	Array::Ptr segments = GetSegments();

	if (segments) {
		/* Try to merge the new segment into an existing segment. */
		ObjectLock dlock(segments);
		for (const Dictionary::Ptr& segment : segments) {
			if (segment->Get("begin") <= begin && segment->Get("end") >= end)
				return; /* New segment is fully contained in this segment. */

			if (segment->Get("begin") >= begin && segment->Get("end") <= end) {
				segment->Set("begin", begin);
				segment->Set("end", end); /* Extend an existing segment to both sides */
				return;
			}

			if (segment->Get("end") >= begin && segment->Get("end") <= end) {
				segment->Set("end", end); /* Extend an existing segment to right. */
				return;
			}

			if (segment->Get("begin") >= begin && segment->Get("begin") <= end) {
				segment->Set("begin", begin); /* Extend an existing segment to left. */
				return;
			}

		}
	}

	/* Create new segment if we weren't able to merge this into an existing segment. */
	Dictionary::Ptr segment = new Dictionary();
	segment->Set("begin", begin);
	segment->Set("end", end);

	if (!segments) {
		segments = new Array();
		SetSegments(segments);
	}

	segments->Add(segment);
}
Example #26
0
static Array::Ptr StringSplit(const String& delims)
{
	ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
	String self = vframe->Self;
	std::vector<String> tokens;
	boost::algorithm::split(tokens, self, boost::is_any_of(delims));

	Array::Ptr result = new Array();
	BOOST_FOREACH(const String& token, tokens) {
		result->Add(token);
	}
Example #27
0
/**
 * Converts a JSON object to an array.
 *
 * @param json The JSON object.
 * @returns An array that is equivalent to the JSON object.
 */
Array::Ptr Array::FromJson(cJSON *json)
{
	Array::Ptr array = make_shared<Array>();

	ASSERT(json->type == cJSON_Array);

	for (cJSON *i = json->child; i != NULL; i = i->next) {
		array->Add(Value::FromJson(i));
	}

	return array;
}
Example #28
0
void Notification::Validate(int types, const ValidationUtils& utils)
{
	ObjectImpl<Notification>::Validate(types, utils);

	if (!(types & FAConfig))
		return;

	Array::Ptr users = GetUsersRaw();
	Array::Ptr groups = GetUserGroupsRaw();

	if ((!users || users->GetLength() == 0) && (!groups || groups->GetLength() == 0))
		BOOST_THROW_EXCEPTION(ValidationError(this, std::vector<String>(), "Validation failed: No users/user_groups specified."));
}
Example #29
0
void ScriptFrame::AddImport(const Object::Ptr& import)
{
	Array::Ptr imports;

	if (!m_Imports)
		imports = new Array();
	else
		imports = m_Imports->ShallowClone();

	imports->Add(import);

	m_Imports = imports;
}
Example #30
0
Array::Ptr ScriptUtils::Keys(const Dictionary::Ptr& dict)
{
	Array::Ptr result = new Array();

	if (dict) {
		ObjectLock olock(dict);
		for (const Dictionary::Pair& kv : dict) {
			result->Add(kv.first);
		}
	}

	return result;
}