<commit_before>/**
* VariableRegistry.cpp
 *
 * History:
 * David Cox on Tue Dec 10 2002 - Created.
 * Paul Jankunas on 4/29/05 - Added the experiment package codec to package.
 *                          Also added the constant code to codec package.
 * Paul Jankunas on 5/17/05 - Removed codec code, it now is stored in a
 *                                                          scarab package.
 * Paul Jankunas on 06/15/05 - Fixed ScarabDatum constructor, added function
 *      addVariable that takes a Variable arg, fixed a bug in getVariable
 *      that was returning empty variables for 0 index.
 *
 * Copyright (c) 2005 MIT. All rights reserved.
 */

#include "VariableRegistry.h"
#include "Utilities.h"
#include "Experiment.h"
#include "EventBuffer.h"
#include "EventConstants.h"
#include "GenericVariable.h"
using namespace mw;

VariableRegistry::VariableRegistry(shared_ptr<EventBuffer> _buffer) {
	event_buffer = _buffer;

    current_unique_code = N_RESERVED_CODEC_CODES;
}

void VariableRegistry::reset(){

    master_variable_list.clear();

    // for faster lookups by tag name
    master_variable_dictionary = map< string, shared_ptr<Variable> >();

	// just the local variables
	local_variable_list.clear();

	// just the global variables
    global_variable_list.clear();

	// just the selection variables
    selection_variable_list.clear();

    //addPlaceholders();
}

VariableRegistry::~VariableRegistry() { }

void VariableRegistry::updateFromCodecDatum(const Datum &codec) {
	mprintf(M_SYSTEM_MESSAGE_DOMAIN,
			"Received new codec, updating variable registry.");

    if(!codec.isDictionary()) {
        merror(M_SYSTEM_MESSAGE_DOMAIN,
			   "Invalid codec received.  Registry is unchanged.");
		return;
	}

	boost::mutex::scoped_lock s_lock(lock);

	master_variable_list.clear();

	// add the placeholders
	//addPlaceholders();

	//////////////////////////////////////////////////////////////////
	// now add what's in the codec

	ScarabDatum *datum = codec.getScarabDatum();

	ScarabDatum ** keys = scarab_dict_keys(datum);
	int size = datum->data.dict->tablesize;

	int maxCodecCode = -1;
	// find the maximum codec value
	for(int i = 0; i < size; ++i) {
		if(keys[i]) {
			long long code = keys[i]->data.integer;
			maxCodecCode = (maxCodecCode < code) ? code : maxCodecCode;
		}
	}

	// add each variable in order to the registry
	for(int i = N_RESERVED_CODEC_CODES; i<=maxCodecCode; ++i) {
		ScarabDatum *key = scarab_new_integer(i);
		ScarabDatum *serializedVariable = scarab_dict_get(datum, key);
		scarab_free_datum(key);

		if(serializedVariable) {
			if(serializedVariable->type != SCARAB_DICT) {
				// these must be  placeholder datums in the package
				// that we should ignore.
				mwarning(M_SYSTEM_MESSAGE_DOMAIN,
						 "Bad variable received from network stream");

                shared_ptr<EmptyVariable> empty_var(new EmptyVariable);
                master_variable_list.push_back(empty_var);
                continue;
			}

			VariableProperties *props =
				new VariableProperties(serializedVariable);

			if(props == NULL){
				mwarning(M_SYSTEM_MESSAGE_DOMAIN,
						 "Bad variable received from network stream");

                shared_ptr<EmptyVariable> empty_var(new EmptyVariable);
                master_variable_list.push_back(empty_var);
                continue;
			}

			shared_ptr<Variable> newvar(new GlobalVariable(props));
			newvar->setCodecCode(i); //necessary? .. Yup

			master_variable_list.push_back(newvar);

            std::string tag = newvar->getVariableName();
            if(!tag.empty()){
                master_variable_dictionary[tag] = newvar;
            }
		}
	}
}

void VariableRegistry::announceLocalVariables(){
	boost::mutex::scoped_lock s_lock(lock);

    vector< shared_ptr<Variable> >::iterator i;
	for(i = local_variable_list.begin(); i != local_variable_list.end(); i++){
		(*i)->announce();
	}
}

void VariableRegistry::announceGlobalVariables(){
	boost::mutex::scoped_lock s_lock(lock);

	vector< shared_ptr<Variable> >::iterator i;
	for(i = global_variable_list.begin(); i != global_variable_list.end(); i++){
		(*i)->announce();
	}

}

void VariableRegistry::announceSelectionVariables(){
	boost::mutex::scoped_lock s_lock(lock);

	vector< shared_ptr<Variable> >::iterator i;
	for(i = selection_variable_list.begin(); i != selection_variable_list.end(); i++){
		(*i)->announce();
	}
}

void VariableRegistry::announceAll() {
	boost::mutex::scoped_lock s_lock(lock);

    vector< shared_ptr<Variable> >::iterator i;
    for(i = master_variable_list.begin(); i != master_variable_list.end(); i++){
		(*i)->announce();
	}
}

// There is potential room for speed up with a hash_map, though this would
// require const char * machinations, as hash_map cannot have string keys
shared_ptr<Variable> VariableRegistry::getVariable(const std::string& tagname) const{
	boost::mutex::scoped_lock s_lock((boost::mutex&)lock);

    map< string, shared_ptr<Variable> >::const_iterator it;
    it = master_variable_dictionary.find(tagname);
    if(it == master_variable_dictionary.end()){
        return shared_ptr<Variable>();
    } else {
        return it->second;
    }

    // This one line is how it would have been written if the stx parser guy
    // didn't have a const-correctness stick up his butt
    //return master_variable_dictionary[tagname];
}

shared_ptr<Variable> VariableRegistry::getVariable(int codec_code) {

	shared_ptr<Variable> var;

	boost::mutex::scoped_lock s_lock((boost::mutex&)lock);

    // DDC: removed what was this for?
	//mExpandableList<Variable> list(master_variable_list);

	if(codec_code < 0 || codec_code > master_variable_list.size() + N_RESERVED_CODEC_CODES){
		merror(M_SYSTEM_MESSAGE_DOMAIN,
			   "Attempt to get an invalid variable (code: %d)",
			   codec_code);

		var = shared_ptr<Variable>();
	} else {
        // DDC: removed copying.  What was that for?
		//var = list[codec_code];
        var = master_variable_list[codec_code - N_RESERVED_CODEC_CODES];
	}

	return var;
}

std::vector<std::string> VariableRegistry::getVariableTagNames() {
	boost::mutex::scoped_lock s_lock(lock);

	std::vector<std::string> tagnames;

    vector< shared_ptr<Variable> >::iterator i;
	for(i = master_variable_list.begin(); i != master_variable_list.end(); i++){
		shared_ptr<Variable> var = (*i);
		if(var) {
			tagnames.push_back(var->getVariableName());
		}
	}

	return tagnames;
}

bool VariableRegistry::hasVariable(const char *tagname) const {
	return (getVariable(tagname) != NULL);
}
bool VariableRegistry::hasVariable(std::string &tagname) const {
	return (getVariable(tagname) != NULL);
}

int VariableRegistry::getNVariables() {
	return master_variable_list.size();
}

shared_ptr<ScopedVariable> VariableRegistry::addScopedVariable(weak_ptr<ScopedVariableEnvironment> env,
																 VariableProperties *props) {
    // create a new entry and return one instance
    if(props == NULL) {
        // TODO warn
		throw SimpleException("Failed to add scoped variable to registry");
    }

    int variable_context_index = -1;
	int codec_code = -1;

	VariableProperties *props_copy = new VariableProperties(*props);

    shared_ptr<ScopedVariable> new_variable(new ScopedVariable(props_copy));
	//GlobalCurrentExperiment->addVariable(new_variable);

    master_variable_list.push_back(new_variable);
	codec_code = master_variable_list.size() + N_RESERVED_CODEC_CODES - 1;

    std::string tag = new_variable->getVariableName();
    if(!tag.empty()){
        master_variable_dictionary[tag] = new_variable;
	}

    local_variable_list.push_back(new_variable);
	variable_context_index = local_variable_list.size() - 1;

	new_variable->setContextIndex(variable_context_index);
	new_variable->setLogging(M_WHEN_CHANGED);
	new_variable->setCodecCode(codec_code);
	new_variable->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));

	if(!env.expired()){
		shared_ptr<ScopedVariableEnvironment> env_shared(env);
		env_shared->addVariable(new_variable);
	}

	return new_variable;
}

shared_ptr<GlobalVariable> VariableRegistry::addGlobalVariable(VariableProperties *props){

	if(props == NULL){
		// TODO: warn
		throw SimpleException("Failed to add global variable to registry");
	}

	VariableProperties *copy = new VariableProperties(*props);
	shared_ptr<GlobalVariable> returnref(new GlobalVariable(copy));

    master_variable_list.push_back(returnref);
	int codec_code = master_variable_list.size() + N_RESERVED_CODEC_CODES - 1;

    std::string tag = returnref->getVariableName();
    if(!tag.empty()){
        master_variable_dictionary[tag] = returnref;
	}

    global_variable_list.push_back(returnref);

	returnref->setCodecCode(codec_code);
	returnref->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));

	return returnref;
}

shared_ptr<ConstantVariable> VariableRegistry::addConstantVariable(Datum value){

	shared_ptr<ConstantVariable> returnref(new ConstantVariable(value));
	returnref->setCodecCode(-1);
	returnref->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));

	return returnref;
}

shared_ptr<Timer> VariableRegistry::createTimer(VariableProperties *props) {
	VariableProperties *props_copy;

	if(props != NULL){
		props_copy = new VariableProperties(*props);
	} else {
		props_copy = NULL;
	}

	shared_ptr<Timer> new_timer(new Timer(props_copy));

//	int codec_code = master_variable_list.addReference(new_timer);

	std::string tag = new_timer->getVariableName();
	if(!tag.empty()){
        master_variable_dictionary[tag] = new_timer;
	}

	new_timer->setCodecCode(-1);
	new_timer->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));

	return new_timer;
}

//shared_ptr<EmptyVariable> VariableRegistry::addPlaceholderVariable(VariableProperties *props){
//
//	VariableProperties *props_copy;
//
//	if(props != NULL){
//		props_copy = new VariableProperties(*props);
//	} else {
//		props_copy = NULL;
//	}
//
//
//	shared_ptr<EmptyVariable> returnref(new EmptyVariable(props_copy));
//
//    master_variable_list.push_back(returnref);
//	int codec_code = master_variable_list.size();
//
//	returnref->setCodecCode(codec_code);
//	returnref->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));
//
//	return returnref;
//}

shared_ptr<SelectionVariable> VariableRegistry::addSelectionVariable(VariableProperties *props){

	VariableProperties *props_copy;

	if(props != NULL){
		props_copy = new VariableProperties(*props);
	} else {
		props_copy = NULL;
	}

	shared_ptr<SelectionVariable> returnref(new SelectionVariable(props_copy));

    master_variable_list.push_back(returnref);
	int codec_code = master_variable_list.size();

	std::string tag = returnref->getVariableName();
    if(!tag.empty()){
        master_variable_dictionary[tag] = returnref;
	}

    selection_variable_list.push_back(returnref);

	returnref->setCodecCode(codec_code);
	returnref->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));
	return returnref;
}

shared_ptr<ScopedVariable> VariableRegistry::createScopedVariable(weak_ptr<ScopedVariableEnvironment> env,
																	VariableProperties *props) {

    boost::mutex::scoped_lock s_lock(lock);

	shared_ptr<ScopedVariable> var = addScopedVariable(env, props);

	return var;
}

shared_ptr<GlobalVariable> VariableRegistry::createGlobalVariable(VariableProperties *props) {
    boost::mutex::scoped_lock s_lock(lock);

	shared_ptr<GlobalVariable> var = addGlobalVariable(props);

	return var;
}

shared_ptr<ConstantVariable> VariableRegistry::createConstantVariable(Datum value){
	return addConstantVariable(value);
}

//shared_ptr<EmptyVariable> VariableRegistry::createPlaceholderVariable(VariableProperties *props){
//    boost::mutex::scoped_lock s_lock(lock);
//
//	shared_ptr<EmptyVariable> var = addPlaceholderVariable(props);
//
//	return var;
//}

shared_ptr<SelectionVariable> VariableRegistry::createSelectionVariable(VariableProperties *props){
    boost::mutex::scoped_lock s_lock(lock);

	shared_ptr<SelectionVariable> var = addSelectionVariable(props);

	return var;
}

Datum VariableRegistry::generateCodecDatum() {

	ScarabDatum *codec = NULL;
    shared_ptr<Variable> var;

	boost::mutex::scoped_lock s_lock(lock);

    int dictSize = master_variable_list.size();

	codec = scarab_dict_new(dictSize, &scarab_dict_times2);

    for(int i = 0; i < dictSize; i++) {
        var = master_variable_list[i];

		if(var == NULL) {
            continue;
        }

		int codec_code = var->getCodecCode();

		if(codec_code == RESERVED_CODEC_CODE) {
			continue;
		}

		VariableProperties *props = var->getProperties();

        if(props == NULL){
            continue;
        }

        Datum serialized_var(props->operator Datum());

        if(serialized_var.isUndefined()) {
            mdebug("local parameter null value at param (%d)", i);
        }
		ScarabDatum *codec_key = scarab_new_integer(codec_code);
        scarab_dict_put(codec, codec_key, serialized_var.getScarabDatum());
		scarab_free_datum(codec_key);
    }

    Datum returnCodec(codec);
	scarab_free_datum(codec);
    return returnCodec;
}

/// Return the (constant) value of a variable.
stx::AnyScalar	VariableRegistry::lookupVariable(const std::string &varname) const{

	shared_ptr<Variable> var = getVariable(varname);
	if(var == NULL){
		// TODO: throw better
		throw  SimpleException("Failed to find variable during expression evaluation", varname);
	}

 Datum value = *(var);
	stx::AnyScalar retval = value;
	return retval;

}

namespace mw {
shared_ptr<VariableRegistry> global_variable_registry;
static bool registry_initialized = false;
}

//void initializeVariableRegistry() {
//	global_variable_registry = shared_ptr<VariableRegistry>(new VariableRegistry(global_outgoing_event_buffer));
//    registry_initialized = true;
//
//}
<commit_msg>Added an additional two lines worth of band-aid for now; a fuller fix has been started in the codec_modernization branch<commit_after>/**
* VariableRegistry.cpp
 *
 * History:
 * David Cox on Tue Dec 10 2002 - Created.
 * Paul Jankunas on 4/29/05 - Added the experiment package codec to package.
 *                          Also added the constant code to codec package.
 * Paul Jankunas on 5/17/05 - Removed codec code, it now is stored in a
 *                                                          scarab package.
 * Paul Jankunas on 06/15/05 - Fixed ScarabDatum constructor, added function
 *      addVariable that takes a Variable arg, fixed a bug in getVariable
 *      that was returning empty variables for 0 index.
 *
 * Copyright (c) 2005 MIT. All rights reserved.
 */

#include "VariableRegistry.h"
#include "Utilities.h"
#include "Experiment.h"
#include "EventBuffer.h"
#include "EventConstants.h"
#include "GenericVariable.h"
using namespace mw;

VariableRegistry::VariableRegistry(shared_ptr<EventBuffer> _buffer) {
	event_buffer = _buffer;

    current_unique_code = N_RESERVED_CODEC_CODES;
}

void VariableRegistry::reset(){

    master_variable_list.clear();

    // for faster lookups by tag name
    master_variable_dictionary = map< string, shared_ptr<Variable> >();

	// just the local variables
	local_variable_list.clear();

	// just the global variables
    global_variable_list.clear();

	// just the selection variables
    selection_variable_list.clear();

    //addPlaceholders();
}

VariableRegistry::~VariableRegistry() { }

void VariableRegistry::updateFromCodecDatum(const Datum &codec) {
	mprintf(M_SYSTEM_MESSAGE_DOMAIN,
			"Received new codec, updating variable registry.");

    if(!codec.isDictionary()) {
        merror(M_SYSTEM_MESSAGE_DOMAIN,
			   "Invalid codec received.  Registry is unchanged.");
		return;
	}

	boost::mutex::scoped_lock s_lock(lock);

	master_variable_list.clear();

	// add the placeholders
	//addPlaceholders();

	//////////////////////////////////////////////////////////////////
	// now add what's in the codec

	ScarabDatum *datum = codec.getScarabDatum();

	ScarabDatum ** keys = scarab_dict_keys(datum);
	int size = datum->data.dict->tablesize;

	int maxCodecCode = -1;
	// find the maximum codec value
	for(int i = 0; i < size; ++i) {
		if(keys[i]) {
			long long code = keys[i]->data.integer;
			maxCodecCode = (maxCodecCode < code) ? code : maxCodecCode;
		}
	}

	// add each variable in order to the registry
	for(int i = N_RESERVED_CODEC_CODES; i<=maxCodecCode; ++i) {
		ScarabDatum *key = scarab_new_integer(i);
		ScarabDatum *serializedVariable = scarab_dict_get(datum, key);
		scarab_free_datum(key);

		if(!serializedVariable) {
            shared_ptr<EmptyVariable> empty_var(new EmptyVariable);
            master_variable_list.push_back(empty_var);
            continue;
        } else {
			if(serializedVariable->type != SCARAB_DICT) {
				// these must be  placeholder datums in the package
				// that we should ignore.
				mwarning(M_SYSTEM_MESSAGE_DOMAIN,
						 "Bad variable received from network stream");

                shared_ptr<EmptyVariable> empty_var(new EmptyVariable);
                master_variable_list.push_back(empty_var);
                continue;
			}

			VariableProperties *props =
				new VariableProperties(serializedVariable);

			if(props == NULL){
				mwarning(M_SYSTEM_MESSAGE_DOMAIN,
						 "Bad variable received from network stream");

                shared_ptr<EmptyVariable> empty_var(new EmptyVariable);
                master_variable_list.push_back(empty_var);
                continue;
			}

			shared_ptr<Variable> newvar(new GlobalVariable(props));
			newvar->setCodecCode(i); //necessary? .. Yup

			master_variable_list.push_back(newvar);

            std::string tag = newvar->getVariableName();
            if(!tag.empty()){
                master_variable_dictionary[tag] = newvar;
            }
		}
	}
}

void VariableRegistry::announceLocalVariables(){
	boost::mutex::scoped_lock s_lock(lock);

    vector< shared_ptr<Variable> >::iterator i;
	for(i = local_variable_list.begin(); i != local_variable_list.end(); i++){
		(*i)->announce();
	}
}

void VariableRegistry::announceGlobalVariables(){
	boost::mutex::scoped_lock s_lock(lock);

	vector< shared_ptr<Variable> >::iterator i;
	for(i = global_variable_list.begin(); i != global_variable_list.end(); i++){
		(*i)->announce();
	}

}

void VariableRegistry::announceSelectionVariables(){
	boost::mutex::scoped_lock s_lock(lock);

	vector< shared_ptr<Variable> >::iterator i;
	for(i = selection_variable_list.begin(); i != selection_variable_list.end(); i++){
		(*i)->announce();
	}
}

void VariableRegistry::announceAll() {
	boost::mutex::scoped_lock s_lock(lock);

    vector< shared_ptr<Variable> >::iterator i;
    for(i = master_variable_list.begin(); i != master_variable_list.end(); i++){
		(*i)->announce();
	}
}

// There is potential room for speed up with a hash_map, though this would
// require const char * machinations, as hash_map cannot have string keys
shared_ptr<Variable> VariableRegistry::getVariable(const std::string& tagname) const{
	boost::mutex::scoped_lock s_lock((boost::mutex&)lock);

    map< string, shared_ptr<Variable> >::const_iterator it;
    it = master_variable_dictionary.find(tagname);
    if(it == master_variable_dictionary.end()){
        return shared_ptr<Variable>();
    } else {
        return it->second;
    }

    // This one line is how it would have been written if the stx parser guy
    // didn't have a const-correctness stick up his butt
    //return master_variable_dictionary[tagname];
}

shared_ptr<Variable> VariableRegistry::getVariable(int codec_code) {

	shared_ptr<Variable> var;

	boost::mutex::scoped_lock s_lock((boost::mutex&)lock);

    // DDC: removed what was this for?
	//mExpandableList<Variable> list(master_variable_list);

	if(codec_code < 0 || codec_code > master_variable_list.size() + N_RESERVED_CODEC_CODES){
		merror(M_SYSTEM_MESSAGE_DOMAIN,
			   "Attempt to get an invalid variable (code: %d)",
			   codec_code);

		var = shared_ptr<Variable>();
	} else {
        // DDC: removed copying.  What was that for?
		//var = list[codec_code];
        var = master_variable_list[codec_code - N_RESERVED_CODEC_CODES];
	}

	return var;
}

std::vector<std::string> VariableRegistry::getVariableTagNames() {
	boost::mutex::scoped_lock s_lock(lock);

	std::vector<std::string> tagnames;

    vector< shared_ptr<Variable> >::iterator i;
	for(i = master_variable_list.begin(); i != master_variable_list.end(); i++){
		shared_ptr<Variable> var = (*i);
		if(var) {
			tagnames.push_back(var->getVariableName());
		}
	}

	return tagnames;
}

bool VariableRegistry::hasVariable(const char *tagname) const {
	return (getVariable(tagname) != NULL);
}
bool VariableRegistry::hasVariable(std::string &tagname) const {
	return (getVariable(tagname) != NULL);
}

int VariableRegistry::getNVariables() {
	return master_variable_list.size();
}

shared_ptr<ScopedVariable> VariableRegistry::addScopedVariable(weak_ptr<ScopedVariableEnvironment> env,
																 VariableProperties *props) {
    // create a new entry and return one instance
    if(props == NULL) {
        // TODO warn
		throw SimpleException("Failed to add scoped variable to registry");
    }

    int variable_context_index = -1;
	int codec_code = -1;

	VariableProperties *props_copy = new VariableProperties(*props);

    shared_ptr<ScopedVariable> new_variable(new ScopedVariable(props_copy));
	//GlobalCurrentExperiment->addVariable(new_variable);

    master_variable_list.push_back(new_variable);
	codec_code = master_variable_list.size() + N_RESERVED_CODEC_CODES - 1;

    std::string tag = new_variable->getVariableName();
    if(!tag.empty()){
        master_variable_dictionary[tag] = new_variable;
	}

    local_variable_list.push_back(new_variable);
	variable_context_index = local_variable_list.size() - 1;

	new_variable->setContextIndex(variable_context_index);
	new_variable->setLogging(M_WHEN_CHANGED);
	new_variable->setCodecCode(codec_code);
	new_variable->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));

	if(!env.expired()){
		shared_ptr<ScopedVariableEnvironment> env_shared(env);
		env_shared->addVariable(new_variable);
	}

	return new_variable;
}

shared_ptr<GlobalVariable> VariableRegistry::addGlobalVariable(VariableProperties *props){

	if(props == NULL){
		// TODO: warn
		throw SimpleException("Failed to add global variable to registry");
	}

	VariableProperties *copy = new VariableProperties(*props);
	shared_ptr<GlobalVariable> returnref(new GlobalVariable(copy));

    master_variable_list.push_back(returnref);
	int codec_code = master_variable_list.size() + N_RESERVED_CODEC_CODES - 1;

    std::string tag = returnref->getVariableName();
    if(!tag.empty()){
        master_variable_dictionary[tag] = returnref;
	}

    global_variable_list.push_back(returnref);

	returnref->setCodecCode(codec_code);
	returnref->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));

	return returnref;
}

shared_ptr<ConstantVariable> VariableRegistry::addConstantVariable(Datum value){

	shared_ptr<ConstantVariable> returnref(new ConstantVariable(value));
	returnref->setCodecCode(-1);
	returnref->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));

	return returnref;
}

shared_ptr<Timer> VariableRegistry::createTimer(VariableProperties *props) {
	VariableProperties *props_copy;

	if(props != NULL){
		props_copy = new VariableProperties(*props);
	} else {
		props_copy = NULL;
	}

	shared_ptr<Timer> new_timer(new Timer(props_copy));

//	int codec_code = master_variable_list.addReference(new_timer);

	std::string tag = new_timer->getVariableName();
	if(!tag.empty()){
        master_variable_dictionary[tag] = new_timer;
	}

	new_timer->setCodecCode(-1);
	new_timer->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));

	return new_timer;
}

//shared_ptr<EmptyVariable> VariableRegistry::addPlaceholderVariable(VariableProperties *props){
//
//	VariableProperties *props_copy;
//
//	if(props != NULL){
//		props_copy = new VariableProperties(*props);
//	} else {
//		props_copy = NULL;
//	}
//
//
//	shared_ptr<EmptyVariable> returnref(new EmptyVariable(props_copy));
//
//    master_variable_list.push_back(returnref);
//	int codec_code = master_variable_list.size();
//
//	returnref->setCodecCode(codec_code);
//	returnref->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));
//
//	return returnref;
//}

shared_ptr<SelectionVariable> VariableRegistry::addSelectionVariable(VariableProperties *props){

	VariableProperties *props_copy;

	if(props != NULL){
		props_copy = new VariableProperties(*props);
	} else {
		props_copy = NULL;
	}

	shared_ptr<SelectionVariable> returnref(new SelectionVariable(props_copy));

    master_variable_list.push_back(returnref);
	int codec_code = master_variable_list.size();

	std::string tag = returnref->getVariableName();
    if(!tag.empty()){
        master_variable_dictionary[tag] = returnref;
	}

    selection_variable_list.push_back(returnref);

	returnref->setCodecCode(codec_code);
	returnref->setEventTarget(static_pointer_cast<EventReceiver>(event_buffer));
	return returnref;
}

shared_ptr<ScopedVariable> VariableRegistry::createScopedVariable(weak_ptr<ScopedVariableEnvironment> env,
																	VariableProperties *props) {

    boost::mutex::scoped_lock s_lock(lock);

	shared_ptr<ScopedVariable> var = addScopedVariable(env, props);

	return var;
}

shared_ptr<GlobalVariable> VariableRegistry::createGlobalVariable(VariableProperties *props) {
    boost::mutex::scoped_lock s_lock(lock);

	shared_ptr<GlobalVariable> var = addGlobalVariable(props);

	return var;
}

shared_ptr<ConstantVariable> VariableRegistry::createConstantVariable(Datum value){
	return addConstantVariable(value);
}

//shared_ptr<EmptyVariable> VariableRegistry::createPlaceholderVariable(VariableProperties *props){
//    boost::mutex::scoped_lock s_lock(lock);
//
//	shared_ptr<EmptyVariable> var = addPlaceholderVariable(props);
//
//	return var;
//}

shared_ptr<SelectionVariable> VariableRegistry::createSelectionVariable(VariableProperties *props){
    boost::mutex::scoped_lock s_lock(lock);

	shared_ptr<SelectionVariable> var = addSelectionVariable(props);

	return var;
}

Datum VariableRegistry::generateCodecDatum() {

	ScarabDatum *codec = NULL;
    shared_ptr<Variable> var;

	boost::mutex::scoped_lock s_lock(lock);

    int dictSize = master_variable_list.size();

	codec = scarab_dict_new(dictSize, &scarab_dict_times2);

    for(int i = 0; i < dictSize; i++) {
        var = master_variable_list[i];

		if(var == NULL) {
            continue;
        }

		int codec_code = var->getCodecCode();

		if(codec_code == RESERVED_CODEC_CODE) {
			continue;
		}

		VariableProperties *props = var->getProperties();

        if(props == NULL){
            continue;
        }

        Datum serialized_var(props->operator Datum());

        if(serialized_var.isUndefined()) {
            mdebug("local parameter null value at param (%d)", i);
        }
		ScarabDatum *codec_key = scarab_new_integer(codec_code);
        scarab_dict_put(codec, codec_key, serialized_var.getScarabDatum());
		scarab_free_datum(codec_key);
    }

    Datum returnCodec(codec);
	scarab_free_datum(codec);
    return returnCodec;
}

/// Return the (constant) value of a variable.
stx::AnyScalar	VariableRegistry::lookupVariable(const std::string &varname) const{

	shared_ptr<Variable> var = getVariable(varname);
	if(var == NULL){
		// TODO: throw better
		throw  SimpleException("Failed to find variable during expression evaluation", varname);
	}

 Datum value = *(var);
	stx::AnyScalar retval = value;
	return retval;

}

namespace mw {
shared_ptr<VariableRegistry> global_variable_registry;
static bool registry_initialized = false;
}

//void initializeVariableRegistry() {
//	global_variable_registry = shared_ptr<VariableRegistry>(new VariableRegistry(global_outgoing_event_buffer));
//    registry_initialized = true;
//
//}
<|endoftext|>


<commit_before>#include "susi/cluster/ClusterComponent.h"

void Susi::ClusterComponent::addToBlacklist(std::string & id){
	eventBlacklist.push_front(id);
	while(eventBlacklist.size() > 64){
		eventBlacklist.pop_back();
	}
}

bool Susi::ClusterComponent::checkIfInBlacklist(std::string & id){
	for(auto & entry : eventBlacklist){
		if(entry == id){
			return true;
		}
	}
	return false;
}

void Susi::ClusterComponent::validateNode(BSON::Value & node){
	if(!node.isObject())
		throw std::runtime_error{"ClusterComponent: Node config not valid: config is not an object."};
	if(!node["id"].isString())
		throw std::runtime_error{"ClusterComponent: Node config not valid: the field 'id' is not present or not string."};
	if(!node["processors"].isUndefined()){
		if(!node["processors"].isArray())
			throw std::runtime_error{"ClusterComponent: Node config not valid: the field 'processors' is not an array."};
		for(auto & val : node["processors"].getArray()){
			if(!val.isString())
				throw std::runtime_error{"ClusterComponent: Node config not valid: one entry in 'processors' is not string."};
		}
	}
	if(!node["consumers"].isUndefined()){
		if(!node["consumers"].isArray())
			throw std::runtime_error{"ClusterComponent: Node config not valid: the field 'consumers' is not an array."};
		for(auto & val : node["consumers"].getArray()){
			if(!val.isString())
				throw std::runtime_error{"ClusterComponent: Node config not valid: one entry in 'consumers' is not string."};
		}
	}
	if(!node["forward"].isUndefined()){
		if(!node["forward"].isArray())
			throw std::runtime_error{"ClusterComponent: Node config not valid: the field 'forward' is not an array."};
		for(auto & val : node["forward"].getArray()){
			if(!val.isString())
				throw std::runtime_error{"ClusterComponent: Node config not valid: one entry in 'forward' is not string."};
		}
	}
}

void Susi::ClusterComponent::setupNode(BSON::Value & node){
	LOG(DEBUG) << "configure cluster node: "<<node.toJSON();
	std::string id = node["id"];
	auto apiClient = std::make_shared<Susi::Api::ApiClient>(node["addr"].getString());
	apiClients[id] = apiClient;

	apiClient->registerConsumer("connection::connect",[this,id](Susi::Events::SharedEventPtr evt){
		LOG(INFO) << "ClusterComponent: node " << id <<" is now online";
		auto event = createEvent("cluster::node::open");
		event->payload = id;
		publish(std::move(event));
	});

	apiClient->registerConsumer("connection::close",[this,id](Susi::Events::SharedEventPtr evt){
		LOG(INFO) << "ClusterComponent: node " << id <<" is now offline";
		auto event = createEvent("cluster::node::close");
		event->payload = id;
		publish(std::move(event));
	});

	//forward all events "*@$NODE_ID" to this node.
	registerForwardingForNode(id,"*@"+id);

	if(!node["processors"].isUndefined()){
		for(std::string & topic : node["processors"].getArray()){
			registerProcessorForNode(id,topic);
		}
	}
	if(!node["consumers"].isUndefined()){
		for(std::string & topic : node["consumers"].getArray()){
			registerConsumerForNode(id,topic);
		}
	}
	if(!node["forward"].isUndefined()){
		for(std::string & topic : node["forward"].getArray()){
			registerForwardingForNode(id,topic);
		}
	}
}

void Susi::ClusterComponent::registerProcessorForNode(std::string nodeId, std::string topic){
	LOG(DEBUG) << "setup processor for "<<nodeId<<": "<<topic;
	auto & apiClient = apiClients[nodeId];
	apiClient->registerProcessor(topic,[this,nodeId](Susi::Events::EventPtr remoteEvent){
		if(checkIfInBlacklist(remoteEvent->id)){
			return;
		}
		addToBlacklist(remoteEvent->id);
		struct FinishCallback {
			Susi::Events::EventPtr remoteEvent;
			FinishCallback(Susi::Events::EventPtr evt) :
				remoteEvent{std::move(evt)} {}
			FinishCallback(FinishCallback && other) :
				remoteEvent{std::move(other.remoteEvent)} {}
			FinishCallback(FinishCallback & other) :
				remoteEvent{std::move(other.remoteEvent)} {}
			void operator()(Susi::Events::SharedEventPtr localEvent){
				*remoteEvent = *localEvent;
			}
		};
		auto localEvent = this->createEvent(remoteEvent->topic);
		*localEvent = *remoteEvent;
		FinishCallback finishCallback{std::move(remoteEvent)};
		this->publish(std::move(localEvent),std::move(finishCallback));
	});
}

void Susi::ClusterComponent::registerConsumerForNode(std::string nodeId, std::string topic){
	LOG(DEBUG) << "setup consumer for "<<nodeId<<": "<<topic;
	auto & apiClient = apiClients[nodeId];
	apiClient->registerConsumer(topic,[this,nodeId](Susi::Events::SharedEventPtr remoteEvent){
		if(checkIfInBlacklist(remoteEvent->id)){
			return;
		}
		addToBlacklist(remoteEvent->id);
		auto localEvent = this->createEvent(remoteEvent->topic);
		*localEvent = *remoteEvent;
		this->publish(std::move(localEvent));
	});
}

void Susi::ClusterComponent::registerForwardingForNode(std::string nodeId, std::string topic){
	LOG(DEBUG) << "setup forwarding to "<<nodeId<<": "<<topic;
	this->subscribe(topic,[this,nodeId,topic](Susi::Events::EventPtr localEvent){
		if(checkIfInBlacklist(localEvent->id)){
			return;
		}
		addToBlacklist(localEvent->id);
		LOG(DEBUG) << "Forwarding to "<<nodeId<<": "<<topic;
		struct FinishCallback {
			Susi::Events::EventPtr localEvent;
			FinishCallback(Susi::Events::EventPtr evt) :
				localEvent{std::move(evt)} {}
			FinishCallback(FinishCallback && other) :
				localEvent{std::move(other.localEvent)} {}
			FinishCallback(FinishCallback & other) :
				localEvent{std::move(other.localEvent)} {}
			void operator()(Susi::Events::SharedEventPtr remoteEvent){
				LOG(DEBUG) << "Forwarding finished.";
				*localEvent = *remoteEvent;
			}
		};
		auto & apiClient = apiClients[nodeId];
		auto remoteEvent = apiClient->createEvent(localEvent->topic);
		*remoteEvent = *localEvent;
		FinishCallback finishCallback{std::move(localEvent)};
		apiClient->publish(std::move(remoteEvent),std::move(finishCallback));
	});
}

<commit_msg>[cluster] make code more robust;<commit_after>#include "susi/cluster/ClusterComponent.h"

void Susi::ClusterComponent::addToBlacklist(std::string & id){
	eventBlacklist.push_front(id);
	while(eventBlacklist.size() > 64){
		eventBlacklist.pop_back();
	}
}

bool Susi::ClusterComponent::checkIfInBlacklist(std::string & id){
	for(auto & entry : eventBlacklist){
		if(entry == id){
			return true;
		}
	}
	return false;
}

void Susi::ClusterComponent::validateNode(BSON::Value & node){
	if(!node.isObject())
		throw std::runtime_error{"ClusterComponent: Node config not valid: config is not an object."};
	if(!node["id"].isString())
		throw std::runtime_error{"ClusterComponent: Node config not valid: the field 'id' is not present or not string."};
	if(!node["processors"].isUndefined()){
		if(!node["processors"].isArray())
			throw std::runtime_error{"ClusterComponent: Node config not valid: the field 'processors' is not an array."};
		for(auto & val : node["processors"].getArray()){
			if(!val.isString())
				throw std::runtime_error{"ClusterComponent: Node config not valid: one entry in 'processors' is not string."};
		}
	}
	if(!node["consumers"].isUndefined()){
		if(!node["consumers"].isArray())
			throw std::runtime_error{"ClusterComponent: Node config not valid: the field 'consumers' is not an array."};
		for(auto & val : node["consumers"].getArray()){
			if(!val.isString())
				throw std::runtime_error{"ClusterComponent: Node config not valid: one entry in 'consumers' is not string."};
		}
	}
	if(!node["forward"].isUndefined()){
		if(!node["forward"].isArray())
			throw std::runtime_error{"ClusterComponent: Node config not valid: the field 'forward' is not an array."};
		for(auto & val : node["forward"].getArray()){
			if(!val.isString())
				throw std::runtime_error{"ClusterComponent: Node config not valid: one entry in 'forward' is not string."};
		}
	}
}

void Susi::ClusterComponent::setupNode(BSON::Value & node){
	LOG(DEBUG) << "configure cluster node: "<<node.toJSON();
	std::string id = node["id"];
	auto apiClient = std::make_shared<Susi::Api::ApiClient>(node["addr"].getString());
	apiClients[id] = apiClient;

	apiClient->registerConsumer("connection::connect",[this,id](Susi::Events::SharedEventPtr evt){
		LOG(INFO) << "ClusterComponent: node " << id <<" is now online";
		auto event = createEvent("cluster::node::open");
		event->payload = id;
		publish(std::move(event));
	});

	apiClient->registerConsumer("connection::close",[this,id](Susi::Events::SharedEventPtr evt){
		LOG(INFO) << "ClusterComponent: node " << id <<" is now offline";
		auto event = createEvent("cluster::node::close");
		event->payload = id;
		publish(std::move(event));
	});

	//forward all events "*@$NODE_ID" to this node.
	registerForwardingForNode(id,"*@"+id);

	if(!node["processors"].isUndefined()){
		for(std::string & topic : node["processors"].getArray()){
			registerProcessorForNode(id,topic);
		}
	}
	if(!node["consumers"].isUndefined()){
		for(std::string & topic : node["consumers"].getArray()){
			registerConsumerForNode(id,topic);
		}
	}
	if(!node["forward"].isUndefined()){
		for(std::string & topic : node["forward"].getArray()){
			registerForwardingForNode(id,topic);
		}
	}
}

void Susi::ClusterComponent::registerProcessorForNode(std::string nodeId, std::string topic){
	LOG(DEBUG) << "setup processor for "<<nodeId<<": "<<topic;
	auto & apiClient = apiClients[nodeId];
	apiClient->registerProcessor(topic,[this,nodeId](Susi::Events::EventPtr remoteEvent){
		if(checkIfInBlacklist(remoteEvent->id)){
			return;
		}
		addToBlacklist(remoteEvent->id);
		struct FinishCallback {
			Susi::Events::EventPtr remoteEvent;
			FinishCallback(Susi::Events::EventPtr evt) :
				remoteEvent{std::move(evt)} {}
			FinishCallback(FinishCallback && other) :
				remoteEvent{std::move(other.remoteEvent)} {}
			FinishCallback(FinishCallback & other) :
				remoteEvent{std::move(other.remoteEvent)} {}
			void operator()(Susi::Events::SharedEventPtr localEvent){
				*remoteEvent = *localEvent;
			}
		};
		auto localEvent = this->createEvent(remoteEvent->topic);
		*localEvent = *remoteEvent;
		FinishCallback finishCallback{std::move(remoteEvent)};
		this->publish(std::move(localEvent),std::move(finishCallback));
	});
}

void Susi::ClusterComponent::registerConsumerForNode(std::string nodeId, std::string topic){
	LOG(DEBUG) << "setup consumer for "<<nodeId<<": "<<topic;
	auto & apiClient = apiClients[nodeId];
	apiClient->registerConsumer(topic,[this,nodeId](Susi::Events::SharedEventPtr remoteEvent){
		if(checkIfInBlacklist(remoteEvent->id)){
			return;
		}
		addToBlacklist(remoteEvent->id);
		auto localEvent = this->createEvent(remoteEvent->topic);
		*localEvent = *remoteEvent;
		this->publish(std::move(localEvent));
	});
}

void Susi::ClusterComponent::registerForwardingForNode(std::string nodeId, std::string topic){
	LOG(DEBUG) << "setup forwarding to "<<nodeId<<": "<<topic;
	this->subscribe(topic,[this,nodeId,topic](Susi::Events::EventPtr localEvent){
		if(checkIfInBlacklist(localEvent->id)){
			return;
		}
		addToBlacklist(localEvent->id);
		LOG(DEBUG) << "Forwarding to "<<nodeId<<": "<<topic;
		struct FinishCallback {
			Susi::Events::EventPtr localEvent;
			FinishCallback(Susi::Events::EventPtr evt) :
				localEvent{std::move(evt)} {}
			FinishCallback(FinishCallback && other) :
				localEvent{std::move(other.localEvent)} {}
			FinishCallback(FinishCallback & other) :
				localEvent{std::move(other.localEvent)} {}
			void operator()(Susi::Events::SharedEventPtr remoteEvent){
				LOG(DEBUG) << "Forwarding finished.";
				if(localEvent){
					*localEvent = *remoteEvent;
				}
			}
		};
		auto & apiClient = apiClients[nodeId];
		auto remoteEvent = apiClient->createEvent(localEvent->topic);
		*remoteEvent = *localEvent;
		FinishCallback finishCallback{std::move(localEvent)};
		apiClient->publish(std::move(remoteEvent),std::move(finishCallback));
	});
}

<|endoftext|>


<commit_before>#include "TailGenerator.h"

TailGenerator::TailGenerator(Context* context) :
                Drawable(context, DRAWABLE_GEOMETRY)
{

    matchNode_ = false;

    geometry_ = SharedPtr<Geometry>(new Geometry(context));
    vertexBuffer_ = SharedPtr<VertexBuffer>(new VertexBuffer(context));
    indexBuffer_ = SharedPtr<IndexBuffer>(new IndexBuffer(context));

    geometry_->SetVertexBuffer(0, vertexBuffer_);
    geometry_->SetIndexBuffer(indexBuffer_);

    indexBuffer_->SetShadowed(false);

    transforms_[0] = Matrix3x4::IDENTITY;
    transforms_[1] = Matrix3x4(Vector3::ZERO, Quaternion(0, 0, 0), Vector3::ONE);

    batches_.Resize(1);
    batches_[0].geometry_ = geometry_;
    batches_[0].geometryType_ = GEOM_STATIC;
    batches_[0].worldTransform_ = &transforms_[0];
    batches_[0].numWorldTransforms_ = 2;

    forceUpdateVertexBuffer_ = false;
    previousPosition_ = Vector3::ZERO;

    tailNum_ = 10;
    // for debug
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    SetMaterial(cache->GetResource<Material>("Materials/TailGenerator.xml"));
    tailLength_ = 0.25f;
    scale_ = 1.0f; // default side scale
    tailTipColor = Color(1.0f, 1.0f, 1.0f, 1.0f);
    tailHeadColor = Color(1.0f, 1.0f, 1.0f, 1.0f);

    forceUpdateVertexBuffer_ = false;

    bbmax = Vector3::ZERO;
    bbmin = Vector3::ZERO;
    vertical_ = horizontal_ = true;
}

TailGenerator::~TailGenerator()
{
}

void TailGenerator::RegisterObject(Context* context)
{
    context->RegisterFactory<TailGenerator>();

    URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Segments", GetNumTails, SetNumTails, unsigned int, 10, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Length", GetTailLength, SetTailLength, float, 0.25f, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Width", GetWidthScale, SetWidthScale, float, 1.0f, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Start Color", GetColorForHead, SetColorForHead, Color, Color::WHITE, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("End Color", GetColorForTip, SetColorForTip, Color, Color::WHITE, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Draw Vertical", GetDrawVertical, SetDrawVertical, bool, true, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Draw Horizontal", GetDrawHorizontal, SetDrawHorizontal, bool, true, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Match Node Rotation", GetMatchNodeOrientation, SetMatchNodeOrientation, bool, false, AM_DEFAULT);
}

void TailGenerator::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
{
    // If no billboard-level testing, use the Drawable test
    if (query.level_ < RAY_TRIANGLE)
    {
        Drawable::ProcessRayQuery(query, results);
        return;
    }
}

void TailGenerator::Update(const FrameInfo &frame)
{
    Drawable::Update(frame);
}

void TailGenerator::UpdateTail()
{
    Vector3 wordPosition = node_->GetWorldPosition();
    float path = (previousPosition_ - wordPosition).Length();

    if (path > tailLength_)
    {
        // новая точка пути
        Tail newPoint;
        newPoint.position = wordPosition;

        Vector3 forwardmotion = matchNode_ ? GetNode()->GetWorldDirection() : (previousPosition_ - wordPosition).Normalized();
        Vector3 rightmotion = matchNode_ ? GetNode()->GetWorldRight() : forwardmotion.CrossProduct(Vector3::UP);
        rightmotion.Normalize();
        newPoint.worldRight = rightmotion;
        newPoint.forward = forwardmotion;

        //forceBuildMeshInWorkerThread_ = true;
        forceUpdateVertexBuffer_ = true;
        previousPosition_ = wordPosition;
        fullPointPath.Push(newPoint);    // Весь путь, все точки за все время работы компонента.
        //knots.Push(wordPosition);        // Для сплайна опорные
    }
}

void  TailGenerator::DrawDebugGeometry(DebugRenderer *debug, bool depthTest)
{
    Drawable::DrawDebugGeometry(debug, depthTest);

    debug->AddNode(node_);

    for (unsigned i = 0; i < tails_.Size()-1; i++)
    {
        debug->AddLine(tails_[i].position, tails_[i+1].position, Color(1,1,1).ToUInt(), false);
    }
}

void TailGenerator::UpdateBatches(const FrameInfo& frame)
{
    // Update tail's mesh if needed
    UpdateTail();

    // Update information for renderer about this drawable
    distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center());
    batches_[0].distance_ = distance_;

    //batches_[0].numWorldTransforms_ = 2;

    // TailGenerator positioning
    //transforms_[0] = Matrix3x4::IDENTITY;
    // TailGenerator rotation
    //transforms_[1] = Matrix3x4(Vector3::ZERO, Quaternion(0, 0, 0), Vector3::ONE);
}

void TailGenerator::UpdateGeometry(const FrameInfo& frame)
{
    if (bufferSizeDirty_ || indexBuffer_->IsDataLost())
        UpdateBufferSize();

    if (bufferDirty_ || vertexBuffer_->IsDataLost() || forceUpdateVertexBuffer_)
        UpdateVertexBuffer(frame);
}

UpdateGeometryType TailGenerator::GetUpdateGeometryType()
{
    if (bufferDirty_ || bufferSizeDirty_ || vertexBuffer_->IsDataLost() || indexBuffer_->IsDataLost()|| forceUpdateVertexBuffer_)
        return UPDATE_MAIN_THREAD;
    else
        return UPDATE_NONE;
}

void TailGenerator::SetMaterial(Material* material)
{
    batches_[0].material_ = material;
    MarkNetworkUpdate();
}

void TailGenerator::OnNodeSet(Node* node)
{
    Drawable::OnNodeSet(node);
}

void TailGenerator::OnWorldBoundingBoxUpdate()
{
    //worldBoundingBox_.Define(-M_LARGE_VALUE, M_LARGE_VALUE);
    worldBoundingBox_.Merge(bbmin);
    worldBoundingBox_.Merge(bbmax);
    worldBoundingBox_.Merge(node_->GetWorldPosition());
}

/// Resize TailGenerator vertex and index buffers.
void TailGenerator::UpdateBufferSize()
{
    unsigned numTails = tailNum_;

    if (!numTails)
        return;

    int vertsPerSegment = (vertical_ && horizontal_ ? 4 : (!vertical_ && !horizontal_ ? 0 : 2));
    int degenerateVertCt = 0;
    if (vertsPerSegment > 2)
        degenerateVertCt += 2; //requires two degenerate triangles

    if (vertexBuffer_->GetVertexCount() != (numTails * vertsPerSegment))
    {
        vertexBuffer_->SetSize((numTails * vertsPerSegment), MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);

    }
    if (indexBuffer_->GetIndexCount() != (numTails * vertsPerSegment) + degenerateVertCt)
    {
        indexBuffer_->SetSize((numTails * vertsPerSegment) + degenerateVertCt, false);
    }

    bufferSizeDirty_ = false;
    bufferDirty_ = true;

    // Indices do not change for a given tail generator capacity
    unsigned short* dest = (unsigned short*)indexBuffer_->Lock(0, (numTails * vertsPerSegment) + degenerateVertCt, true);
    if (!dest)
        return;

    unsigned vertexIndex = 0;
    if (horizontal_)
    {
        unsigned stripsLen = numTails;
        while (stripsLen--)
        {

            dest[0] = vertexIndex;
            dest[1] = vertexIndex + 1;
            dest += 2;

            // degenerate triangle vert on horizontal
            if (vertical_ && stripsLen == 0)
            {
                dest[0] = vertexIndex + 1;
                dest += 1;
            }
            vertexIndex += 2;
        }
    }
    if (vertical_)
    {
        unsigned stripsLen = numTails;
        while (stripsLen--)
        {
            // degenerate triangle vert on vertical
            if (horizontal_ && stripsLen == (numTails - 1))
            {
                dest[0] = vertexIndex;
                dest += 1;
            }

            dest[0] = vertexIndex;
            dest[1] = vertexIndex + 1;
            dest += 2;
            vertexIndex += 2;
        }
    }

    indexBuffer_->Unlock();
    indexBuffer_->ClearDataLost();
}

/// Rewrite TailGenerator vertex buffer.
void TailGenerator::UpdateVertexBuffer(const FrameInfo& frame)
{
    unsigned fullPointPathSize = fullPointPath.Size();
    unsigned currentVisiblePathSize = tailNum_;

    // Clear previous mesh data
    tailMesh.Clear();

    // build tail

    // if tail path is short and nothing to draw, exit
    if (fullPointPathSize < 2) return;

    activeTails.Clear();

    unsigned min_i = fullPointPathSize < currentVisiblePathSize ? 0 : fullPointPathSize - currentVisiblePathSize;
    // Step 1 : collect actual point's info for build tail path
    for (unsigned i = min_i; i < fullPointPathSize - 1; i++)
    {
        activeTails.Push(fullPointPath[i]);

        Vector3 &p = fullPointPath[i].position;

        // Math BoundingBox based on actual point
        if (p.x_ < bbmin.x_) bbmin.x_ = p.x_;
        if (p.y_ < bbmin.y_) bbmin.y_ = p.y_;
        if (p.z_ < bbmin.z_) bbmin.z_ = p.z_;

        if (p.x_ > bbmax.x_) bbmax.x_ = p.x_;
        if (p.y_ > bbmax.y_) bbmax.y_ = p.y_;
        if (p.z_ > bbmax.z_) bbmax.z_ = p.z_;
    }

    if (activeTails.Size() < 2) return;

    Vector<Tail> &t = activeTails;

    // generate strips of tris
    TailVertex v;

    float mixFactor = 1.0f / activeTails.Size();

    // Forward part of tail (strip in xz plane)
    if (horizontal_)
    {
        for (unsigned i = 0; i < activeTails.Size() || i < tailNum_; ++i)
        {
            unsigned sub = i < activeTails.Size() ? i : activeTails.Size() - 1;
            Color c = tailTipColor.Lerp(tailHeadColor, mixFactor * i);
            v.color_ = c.ToUInt();
            v.uv_ = Vector2(1.0f, 0.0f);
            v.position_ = t[sub].position + t[sub].worldRight * scale_;
            tailMesh.Push(v);

            //v.color_ = c.ToUInt();
            v.uv_ = Vector2(0.0f, 1.0f);
            v.position_ = t[sub].position - t[sub].worldRight * scale_;
            tailMesh.Push(v);
        }
    }

    // Upper part of tail (strip in xy-plane)
    if (vertical_)
    {
        for (unsigned i = 0; i < activeTails.Size() || i < tailNum_; ++i)
        {
            unsigned sub = i < activeTails.Size() ? i : activeTails.Size() - 1;
            Color c = tailTipColor.Lerp(tailHeadColor, mixFactor * i);
            v.color_ = c.ToUInt();
            v.uv_ = Vector2(1.0f, 0.0f);
            Vector3 up = t[sub].forward.CrossProduct(t[sub].worldRight);
            up.Normalize();
            v.position_ = t[sub].position + up * scale_;
            tailMesh.Push(v);

            //v.color_ = c.ToUInt();
            v.uv_ = Vector2(0.0f, 1.0f);
            v.position_ = t[sub].position - up * scale_;
            tailMesh.Push(v);
        }
    }

    // copy new mesh to vertex buffer
    unsigned meshVertexCount = tailMesh.Size();
    batches_[0].geometry_->SetDrawRange(TRIANGLE_STRIP, 0, meshVertexCount + (horizontal_ && vertical_ ? 2 : 0), false);
    // get pointer
    TailVertex* dest = (TailVertex*)vertexBuffer_->Lock(0, meshVertexCount, true);
    if (!dest)
        return;
    // copy to vertex buffer
    memcpy(dest, &tailMesh[0], tailMesh.Size() * sizeof(TailVertex));

    vertexBuffer_->Unlock();
    vertexBuffer_->ClearDataLost();

    bufferDirty_ = false;

    // unmark flag
    forceUpdateVertexBuffer_ = false;
}

void TailGenerator::SetTailLength(float length)
{

    tailLength_ = length;
}

void TailGenerator::SetColorForTip(const Color& c)
{
    tailTipColor = Color(c.r_, c.g_, c.b_, 0.0f);
}

void TailGenerator::SetColorForHead(const Color& c)
{
    tailHeadColor = Color(c.r_, c.g_, c.b_, 1.0f);
}

void TailGenerator::SetNumTails(unsigned num)
{
    // Prevent negative value being assigned from the editor
    if (num > M_MAX_INT)
        num = 0;

    if (num > MAX_TAILS)
        num = MAX_TAILS;

    bufferSizeDirty_ = true;
    tailNum_ = num;
}

unsigned TailGenerator::GetNumTails()
{
    return tailNum_;
}

void TailGenerator::SetDrawVertical(bool value)
{
    vertical_ = value;
    //SetupBatches();
}

void TailGenerator::SetDrawHorizontal(bool value)
{
    horizontal_ = value;
    //SetupBatches();
}

void TailGenerator::SetMatchNodeOrientation(bool value)
{
    matchNode_ = value;
}

void TailGenerator::MarkPositionsDirty()
{
    Drawable::OnMarkedDirty(node_);
    bufferDirty_ = true;
}

void TailGenerator::SetMaterialAttr(const ResourceRef& value)
{
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    SetMaterial(cache->GetResource<Material>(value.name_));
}

ResourceRef TailGenerator::GetMaterialAttr() const
{
    return GetResourceRef(batches_[0].material_, Material::GetTypeStatic());
}

void TailGenerator::SetWidthScale(float scale)
{
    scale_ = scale;
}<commit_msg>Fix memory leak https://github.com/MonkeyFirst/urho3d-component-tail-generator/issues/4<commit_after>#include "TailGenerator.h"

TailGenerator::TailGenerator(Context* context) :
                Drawable(context, DRAWABLE_GEOMETRY)
{

    matchNode_ = false;

    geometry_ = SharedPtr<Geometry>(new Geometry(context));
    vertexBuffer_ = SharedPtr<VertexBuffer>(new VertexBuffer(context));
    indexBuffer_ = SharedPtr<IndexBuffer>(new IndexBuffer(context));

    geometry_->SetVertexBuffer(0, vertexBuffer_);
    geometry_->SetIndexBuffer(indexBuffer_);

    indexBuffer_->SetShadowed(false);

    transforms_[0] = Matrix3x4::IDENTITY;
    transforms_[1] = Matrix3x4(Vector3::ZERO, Quaternion(0, 0, 0), Vector3::ONE);

    batches_.Resize(1);
    batches_[0].geometry_ = geometry_;
    batches_[0].geometryType_ = GEOM_STATIC;
    batches_[0].worldTransform_ = &transforms_[0];
    batches_[0].numWorldTransforms_ = 2;

    forceUpdateVertexBuffer_ = false;
    previousPosition_ = Vector3::ZERO;

    tailNum_ = 10;
    // for debug
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    SetMaterial(cache->GetResource<Material>("Materials/TailGenerator.xml"));
    tailLength_ = 0.25f;
    scale_ = 1.0f; // default side scale
    tailTipColor = Color(1.0f, 1.0f, 1.0f, 1.0f);
    tailHeadColor = Color(1.0f, 1.0f, 1.0f, 1.0f);

    forceUpdateVertexBuffer_ = false;

    bbmax = Vector3::ZERO;
    bbmin = Vector3::ZERO;
    vertical_ = horizontal_ = true;
}

TailGenerator::~TailGenerator()
{
}

void TailGenerator::RegisterObject(Context* context)
{
    context->RegisterFactory<TailGenerator>();

    URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Segments", GetNumTails, SetNumTails, unsigned int, 10, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Length", GetTailLength, SetTailLength, float, 0.25f, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Width", GetWidthScale, SetWidthScale, float, 1.0f, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Start Color", GetColorForHead, SetColorForHead, Color, Color::WHITE, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("End Color", GetColorForTip, SetColorForTip, Color, Color::WHITE, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Draw Vertical", GetDrawVertical, SetDrawVertical, bool, true, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Draw Horizontal", GetDrawHorizontal, SetDrawHorizontal, bool, true, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Match Node Rotation", GetMatchNodeOrientation, SetMatchNodeOrientation, bool, false, AM_DEFAULT);
}

void TailGenerator::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
{
    // If no billboard-level testing, use the Drawable test
    if (query.level_ < RAY_TRIANGLE)
    {
        Drawable::ProcessRayQuery(query, results);
        return;
    }
}

void TailGenerator::Update(const FrameInfo &frame)
{
    Drawable::Update(frame);
}

void TailGenerator::UpdateTail()
{
    Vector3 wordPosition = node_->GetWorldPosition();
    float path = (previousPosition_ - wordPosition).Length();

    if (path > tailLength_)
    {
        // новая точка пути
        Tail newPoint;
        newPoint.position = wordPosition;

        Vector3 forwardmotion = matchNode_ ? GetNode()->GetWorldDirection() : (previousPosition_ - wordPosition).Normalized();
        Vector3 rightmotion = matchNode_ ? GetNode()->GetWorldRight() : forwardmotion.CrossProduct(Vector3::UP);
        rightmotion.Normalize();
        newPoint.worldRight = rightmotion;
        newPoint.forward = forwardmotion;

        //forceBuildMeshInWorkerThread_ = true;
        forceUpdateVertexBuffer_ = true;
        previousPosition_ = wordPosition;
        fullPointPath.Push(newPoint);    // Весь путь, все точки за все время работы компонента.
        //knots.Push(wordPosition);        // Для сплайна опорные

        if (fullPointPath.Size() > tailNum_)
            fullPointPath.Erase(0, fullPointPath.Size() - tailNum_);
    }
}

void  TailGenerator::DrawDebugGeometry(DebugRenderer *debug, bool depthTest)
{
    Drawable::DrawDebugGeometry(debug, depthTest);

    debug->AddNode(node_);

    for (unsigned i = 0; i < tails_.Size()-1; i++)
    {
        debug->AddLine(tails_[i].position, tails_[i+1].position, Color(1,1,1).ToUInt(), false);
    }
}

void TailGenerator::UpdateBatches(const FrameInfo& frame)
{
    // Update tail's mesh if needed
    UpdateTail();

    // Update information for renderer about this drawable
    distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center());
    batches_[0].distance_ = distance_;

    //batches_[0].numWorldTransforms_ = 2;

    // TailGenerator positioning
    //transforms_[0] = Matrix3x4::IDENTITY;
    // TailGenerator rotation
    //transforms_[1] = Matrix3x4(Vector3::ZERO, Quaternion(0, 0, 0), Vector3::ONE);
}

void TailGenerator::UpdateGeometry(const FrameInfo& frame)
{
    if (bufferSizeDirty_ || indexBuffer_->IsDataLost())
        UpdateBufferSize();

    if (bufferDirty_ || vertexBuffer_->IsDataLost() || forceUpdateVertexBuffer_)
        UpdateVertexBuffer(frame);
}

UpdateGeometryType TailGenerator::GetUpdateGeometryType()
{
    if (bufferDirty_ || bufferSizeDirty_ || vertexBuffer_->IsDataLost() || indexBuffer_->IsDataLost()|| forceUpdateVertexBuffer_)
        return UPDATE_MAIN_THREAD;
    else
        return UPDATE_NONE;
}

void TailGenerator::SetMaterial(Material* material)
{
    batches_[0].material_ = material;
    MarkNetworkUpdate();
}

void TailGenerator::OnNodeSet(Node* node)
{
    Drawable::OnNodeSet(node);
}

void TailGenerator::OnWorldBoundingBoxUpdate()
{
    //worldBoundingBox_.Define(-M_LARGE_VALUE, M_LARGE_VALUE);
    worldBoundingBox_.Merge(bbmin);
    worldBoundingBox_.Merge(bbmax);
    worldBoundingBox_.Merge(node_->GetWorldPosition());
}

/// Resize TailGenerator vertex and index buffers.
void TailGenerator::UpdateBufferSize()
{
    unsigned numTails = tailNum_;

    if (!numTails)
        return;

    int vertsPerSegment = (vertical_ && horizontal_ ? 4 : (!vertical_ && !horizontal_ ? 0 : 2));
    int degenerateVertCt = 0;
    if (vertsPerSegment > 2)
        degenerateVertCt += 2; //requires two degenerate triangles

    if (vertexBuffer_->GetVertexCount() != (numTails * vertsPerSegment))
    {
        vertexBuffer_->SetSize((numTails * vertsPerSegment), MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);

    }
    if (indexBuffer_->GetIndexCount() != (numTails * vertsPerSegment) + degenerateVertCt)
    {
        indexBuffer_->SetSize((numTails * vertsPerSegment) + degenerateVertCt, false);
    }

    bufferSizeDirty_ = false;
    bufferDirty_ = true;

    // Indices do not change for a given tail generator capacity
    unsigned short* dest = (unsigned short*)indexBuffer_->Lock(0, (numTails * vertsPerSegment) + degenerateVertCt, true);
    if (!dest)
        return;

    unsigned vertexIndex = 0;
    if (horizontal_)
    {
        unsigned stripsLen = numTails;
        while (stripsLen--)
        {

            dest[0] = vertexIndex;
            dest[1] = vertexIndex + 1;
            dest += 2;

            // degenerate triangle vert on horizontal
            if (vertical_ && stripsLen == 0)
            {
                dest[0] = vertexIndex + 1;
                dest += 1;
            }
            vertexIndex += 2;
        }
    }
    if (vertical_)
    {
        unsigned stripsLen = numTails;
        while (stripsLen--)
        {
            // degenerate triangle vert on vertical
            if (horizontal_ && stripsLen == (numTails - 1))
            {
                dest[0] = vertexIndex;
                dest += 1;
            }

            dest[0] = vertexIndex;
            dest[1] = vertexIndex + 1;
            dest += 2;
            vertexIndex += 2;
        }
    }

    indexBuffer_->Unlock();
    indexBuffer_->ClearDataLost();
}

/// Rewrite TailGenerator vertex buffer.
void TailGenerator::UpdateVertexBuffer(const FrameInfo& frame)
{
    unsigned fullPointPathSize = fullPointPath.Size();
    unsigned currentVisiblePathSize = tailNum_;

    // Clear previous mesh data
    tailMesh.Clear();

    // build tail

    // if tail path is short and nothing to draw, exit
    if (fullPointPathSize < 2) return;

    activeTails.Clear();

    unsigned min_i = fullPointPathSize < currentVisiblePathSize ? 0 : fullPointPathSize - currentVisiblePathSize;
    // Step 1 : collect actual point's info for build tail path
    for (unsigned i = min_i; i < fullPointPathSize - 1; i++)
    {
        activeTails.Push(fullPointPath[i]);

        Vector3 &p = fullPointPath[i].position;

        // Math BoundingBox based on actual point
        if (p.x_ < bbmin.x_) bbmin.x_ = p.x_;
        if (p.y_ < bbmin.y_) bbmin.y_ = p.y_;
        if (p.z_ < bbmin.z_) bbmin.z_ = p.z_;

        if (p.x_ > bbmax.x_) bbmax.x_ = p.x_;
        if (p.y_ > bbmax.y_) bbmax.y_ = p.y_;
        if (p.z_ > bbmax.z_) bbmax.z_ = p.z_;
    }

    if (activeTails.Size() < 2) return;

    Vector<Tail> &t = activeTails;

    // generate strips of tris
    TailVertex v;

    float mixFactor = 1.0f / activeTails.Size();

    // Forward part of tail (strip in xz plane)
    if (horizontal_)
    {
        for (unsigned i = 0; i < activeTails.Size() || i < tailNum_; ++i)
        {
            unsigned sub = i < activeTails.Size() ? i : activeTails.Size() - 1;
            Color c = tailTipColor.Lerp(tailHeadColor, mixFactor * i);
            v.color_ = c.ToUInt();
            v.uv_ = Vector2(1.0f, 0.0f);
            v.position_ = t[sub].position + t[sub].worldRight * scale_;
            tailMesh.Push(v);

            //v.color_ = c.ToUInt();
            v.uv_ = Vector2(0.0f, 1.0f);
            v.position_ = t[sub].position - t[sub].worldRight * scale_;
            tailMesh.Push(v);
        }
    }

    // Upper part of tail (strip in xy-plane)
    if (vertical_)
    {
        for (unsigned i = 0; i < activeTails.Size() || i < tailNum_; ++i)
        {
            unsigned sub = i < activeTails.Size() ? i : activeTails.Size() - 1;
            Color c = tailTipColor.Lerp(tailHeadColor, mixFactor * i);
            v.color_ = c.ToUInt();
            v.uv_ = Vector2(1.0f, 0.0f);
            Vector3 up = t[sub].forward.CrossProduct(t[sub].worldRight);
            up.Normalize();
            v.position_ = t[sub].position + up * scale_;
            tailMesh.Push(v);

            //v.color_ = c.ToUInt();
            v.uv_ = Vector2(0.0f, 1.0f);
            v.position_ = t[sub].position - up * scale_;
            tailMesh.Push(v);
        }
    }

    // copy new mesh to vertex buffer
    unsigned meshVertexCount = tailMesh.Size();
    batches_[0].geometry_->SetDrawRange(TRIANGLE_STRIP, 0, meshVertexCount + (horizontal_ && vertical_ ? 2 : 0), false);
    // get pointer
    TailVertex* dest = (TailVertex*)vertexBuffer_->Lock(0, meshVertexCount, true);
    if (!dest)
        return;
    // copy to vertex buffer
    memcpy(dest, &tailMesh[0], tailMesh.Size() * sizeof(TailVertex));

    vertexBuffer_->Unlock();
    vertexBuffer_->ClearDataLost();

    bufferDirty_ = false;

    // unmark flag
    forceUpdateVertexBuffer_ = false;
}

void TailGenerator::SetTailLength(float length)
{

    tailLength_ = length;
}

void TailGenerator::SetColorForTip(const Color& c)
{
    tailTipColor = Color(c.r_, c.g_, c.b_, 0.0f);
}

void TailGenerator::SetColorForHead(const Color& c)
{
    tailHeadColor = Color(c.r_, c.g_, c.b_, 1.0f);
}

void TailGenerator::SetNumTails(unsigned num)
{
    // Prevent negative value being assigned from the editor
    if (num > M_MAX_INT)
        num = 0;

    if (num > MAX_TAILS)
        num = MAX_TAILS;

    bufferSizeDirty_ = true;
    tailNum_ = num;
}

unsigned TailGenerator::GetNumTails()
{
    return tailNum_;
}

void TailGenerator::SetDrawVertical(bool value)
{
    vertical_ = value;
    //SetupBatches();
}

void TailGenerator::SetDrawHorizontal(bool value)
{
    horizontal_ = value;
    //SetupBatches();
}

void TailGenerator::SetMatchNodeOrientation(bool value)
{
    matchNode_ = value;
}

void TailGenerator::MarkPositionsDirty()
{
    Drawable::OnMarkedDirty(node_);
    bufferDirty_ = true;
}

void TailGenerator::SetMaterialAttr(const ResourceRef& value)
{
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    SetMaterial(cache->GetResource<Material>(value.name_));
}

ResourceRef TailGenerator::GetMaterialAttr() const
{
    return GetResourceRef(batches_[0].material_, Material::GetTypeStatic());
}

void TailGenerator::SetWidthScale(float scale)
{
    scale_ = scale;
}<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2020.  All rights reserved
 */
//  TEST    Foundation::Containers::Mapping
//      STATUS  Alpha-Late
#include "Stroika/Foundation/StroikaPreComp.h"

#include <iostream>
#include <sstream>

#include "Stroika/Foundation/Containers/Collection.h"

#include "Stroika/Foundation/Containers/Concrete/Mapping_Array.h"
#include "Stroika/Foundation/Containers/Concrete/Mapping_LinkedList.h"
#include "Stroika/Foundation/Containers/Concrete/Mapping_stdmap.h"
#include "Stroika/Foundation/Containers/Concrete/SortedMapping_stdmap.h"
#include "Stroika/Foundation/Containers/Mapping.h"
#include "Stroika/Foundation/Debug/Assertions.h"
#include "Stroika/Foundation/Debug/Trace.h"

#include "../TestCommon/CommonTests_Mapping.h"
#include "../TestHarness/SimpleClass.h"
#include "../TestHarness/TestHarness.h"

using namespace Stroika;
using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Containers;

using Concrete::Mapping_Array;
using Concrete::Mapping_LinkedList;
using Concrete::Mapping_stdmap;

namespace {
    template <typename CONCRETE_CONTAINER>
    void DoTestForConcreteContainer_ ()
    {
        using namespace CommonTests::MappingTests;
        SimpleMappingTest_All_ (DEFAULT_TESTING_SCHEMA<CONCRETE_CONTAINER>{});
        SimpleMappingTest_WithDefaultEqCompaerer_ (DEFAULT_TESTING_SCHEMA<CONCRETE_CONTAINER>{});
    }
    template <typename CONCRETE_CONTAINER, typename FACTORY, typename VALUE_EQUALS_COMPARER_TYPE>
    void DoTestForConcreteContainer_ (FACTORY factory, VALUE_EQUALS_COMPARER_TYPE valueEqualsComparer)
    {
        using namespace CommonTests::MappingTests;
        auto testschema = DEFAULT_TESTING_SCHEMA<CONCRETE_CONTAINER, FACTORY, VALUE_EQUALS_COMPARER_TYPE>{factory, valueEqualsComparer};
        SimpleMappingTest_All_ (testschema);
    }
}

namespace {
    void Test2_SimpleBaseClassConversionTraitsConfusion_ ()
    {
        Debug::TraceContextBumper ctx{L"{}::Test2_SimpleBaseClassConversionTraitsConfusion_"};
        SortedMapping<int, float> xxxyy  = Concrete::SortedMapping_stdmap<int, float> ();
        Mapping<int, float>       xxxyy1 = Concrete::Mapping_stdmap<int, float> ();
    }
}

namespace {
    namespace Test4_MappingCTOROverloads_ {
        namespace xPrivate_ {
            struct A;
            struct B;
            struct A {
                A ()         = default;
                A (const A&) = default;
                A (const B&) {}
            };
            struct B {
                B () = default;
                B (const A&) {}
                B (const B&) = default;
            };
            using Common::KeyValuePair;
            using KEY_TYPE                = int;
            using VALUE_TYPE              = B;
            using CONTAINER_OF_PAIR_KEY_T = Mapping<int, A>;
            using T                       = KeyValuePair<KEY_TYPE, VALUE_TYPE>;
        }
        void DoIt ()
        {
            Debug::TraceContextBumper ctx{L"{}::Test4_MappingCTOROverloads_"};
            using namespace xPrivate_;
            Mapping<int, A> from;

            static_assert (Configuration::IsIterableOfT_v<Mapping<int, A>, KeyValuePair<int, A>>);
            static_assert (Configuration::IsIterableOfT_v<Mapping<int, B>, KeyValuePair<int, B>>);

            Mapping<int, B> to1;
            for (auto i : from) {
                to1.Add (i);
            }
            Mapping<int, B> to2{from};
        }
    }
}

namespace {
    namespace ExampleCTORS_Test_5_ {
        void DoTest ()
        {
            Debug::TraceContextBumper ctx{L"{}::ExampleCTORS_Test_5_"};
            // From Mapping<> CTOR docs
            Collection<pair<int, int>> c;
            std::map<int, int>         m;

            Mapping<int, int> m1 = {pair<int, int>{1, 1}, pair<int, int>{2, 2}, pair<int, int>{3, 2}};
            Mapping<int, int> m2 = m1;
            Mapping<int, int> m3{m1};
            Mapping<int, int> m4{m1.begin (), m1.end ()};
            Mapping<int, int> m5{c};
            Mapping<int, int> m6{m};
            Mapping<int, int> m7{m.begin (), m.end ()};
            Mapping<int, int> m8{move (m1)};
            Mapping<int, int> m9{Common::DeclareEqualsComparer ([] (int l, int r) { return l == r; })};
        }
    }
}

namespace {
    namespace Where_Test_6_ {
        void DoAll ()
        {
            Mapping<int, int> m{KeyValuePair<int, int>{1, 3}, KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{4, 5}, KeyValuePair<int, int>{5, 7}};
            VerifyTestResult ((m.Where ([] (const KeyValuePair<int, int>& value) { return Math::IsPrime (value.fKey); }) == Mapping<int, int>{KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{5, 7}}));
            VerifyTestResult ((m.Where ([] (int key) { return Math::IsPrime (key); }) == Mapping<int, int>{KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{5, 7}}));
        }
    }
}

namespace {
    namespace WithKeys_Test_7_ {
        void DoAll ()
        {
            Mapping<int, int> m{KeyValuePair<int, int>{1, 3}, KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{4, 5}, KeyValuePair<int, int>{5, 7}};
            VerifyTestResult ((m.WithKeys (initializer_list<int>{2, 5}) == Mapping<int, int>{KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{5, 7}}));
        }
    }
}

namespace {
    namespace ClearBug_Test_8_ {
        void DoAll ()
        {
            // https://stroika.atlassian.net/browse/STK-541
            Mapping<int, int> m{KeyValuePair<int, int>{1, 3}, KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{4, 5}, KeyValuePair<int, int>{5, 7}};
            Mapping<int, int> mm{move (m)};
            m.clear ();
        }
    }
}

namespace {
    void DoRegressionTests_ ()
    {
        struct MySimpleClassWithoutComparisonOperators_ComparerWithEquals_ : Common::ComparisonRelationDeclaration<Common::ComparisonRelationType::eEquals> {
            using value_type = SimpleClassWithoutComparisonOperators;
            bool operator() (const value_type& v1, const value_type& v2) const
            {
                return v1.GetValue () == v2.GetValue ();
            }
        };

        DoTestForConcreteContainer_<Mapping<size_t, size_t>> ();
        DoTestForConcreteContainer_<Mapping<SimpleClass, SimpleClass>> ();
        DoTestForConcreteContainer_<Mapping<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators>> (
            [] () { return Mapping<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators> (MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{}); },
            MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{});

        DoTestForConcreteContainer_<Mapping_Array<size_t, size_t>> ();
        DoTestForConcreteContainer_<Mapping_Array<SimpleClass, SimpleClass>> ();
        DoTestForConcreteContainer_<Mapping_Array<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators>> (
            [] () { return Mapping_Array<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators> (MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{}); },
            MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{});

        DoTestForConcreteContainer_<Mapping_LinkedList<size_t, size_t>> ();
        DoTestForConcreteContainer_<Mapping_LinkedList<SimpleClass, SimpleClass>> ();
        // DoTestForConcreteContainer_AllTestsWhichDontRequireComparer_For_Type_<Mapping_LinkedList<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators_MappingTRAITS>> ();
        DoTestForConcreteContainer_<Mapping_LinkedList<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators>> (
            [] () { return Mapping_LinkedList<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators> (MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{}); },
            MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{});

        DoTestForConcreteContainer_<Mapping_stdmap<size_t, size_t>> ();
        DoTestForConcreteContainer_<Mapping_stdmap<SimpleClass, SimpleClass>> ();
        {
            struct MySimpleClassWithoutComparisonOperators_ComparerWithLess_ : Common::ComparisonRelationDeclaration<Common::ComparisonRelationType::eStrictInOrder> {
                using value_type = SimpleClassWithoutComparisonOperators;
                bool operator() (const value_type& v1, const value_type& v2) const
                {
                    return v1.GetValue () < v2.GetValue ();
                }
            };
            DoTestForConcreteContainer_<Mapping_stdmap<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators>> (
                [] () { return Mapping_stdmap<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators> (MySimpleClassWithoutComparisonOperators_ComparerWithLess_{}); },
                //Common::mkEqualsComparerAdapter (MySimpleClassWithoutComparisonOperators_ComparerWithLess_{})
                MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{});
        }

        Test2_SimpleBaseClassConversionTraitsConfusion_ ();

        //Test3_SimpleMappingTest_WhichRequiresExplcitValueComparer ();

        Test4_MappingCTOROverloads_::DoIt ();

        ExampleCTORS_Test_5_::DoTest ();

        Where_Test_6_::DoAll ();
        WithKeys_Test_7_::DoAll ();
        ClearBug_Test_8_::DoAll ();
    }
}

int main ([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[])
{
    Stroika::TestHarness::Setup ();
    return Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
}
<commit_msg>Added mapping AddVsAddIf_Test_9 test<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2020.  All rights reserved
 */
//  TEST    Foundation::Containers::Mapping
//      STATUS  Alpha-Late
#include "Stroika/Foundation/StroikaPreComp.h"

#include <iostream>
#include <sstream>

#include "Stroika/Foundation/Containers/Collection.h"

#include "Stroika/Foundation/Containers/Concrete/Mapping_Array.h"
#include "Stroika/Foundation/Containers/Concrete/Mapping_LinkedList.h"
#include "Stroika/Foundation/Containers/Concrete/Mapping_stdmap.h"
#include "Stroika/Foundation/Containers/Concrete/SortedMapping_stdmap.h"
#include "Stroika/Foundation/Containers/Mapping.h"
#include "Stroika/Foundation/Debug/Assertions.h"
#include "Stroika/Foundation/Debug/Trace.h"

#include "../TestCommon/CommonTests_Mapping.h"
#include "../TestHarness/SimpleClass.h"
#include "../TestHarness/TestHarness.h"

using namespace Stroika;
using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Containers;

using Concrete::Mapping_Array;
using Concrete::Mapping_LinkedList;
using Concrete::Mapping_stdmap;

namespace {
    template <typename CONCRETE_CONTAINER>
    void DoTestForConcreteContainer_ ()
    {
        using namespace CommonTests::MappingTests;
        SimpleMappingTest_All_ (DEFAULT_TESTING_SCHEMA<CONCRETE_CONTAINER>{});
        SimpleMappingTest_WithDefaultEqCompaerer_ (DEFAULT_TESTING_SCHEMA<CONCRETE_CONTAINER>{});
    }
    template <typename CONCRETE_CONTAINER, typename FACTORY, typename VALUE_EQUALS_COMPARER_TYPE>
    void DoTestForConcreteContainer_ (FACTORY factory, VALUE_EQUALS_COMPARER_TYPE valueEqualsComparer)
    {
        using namespace CommonTests::MappingTests;
        auto testschema = DEFAULT_TESTING_SCHEMA<CONCRETE_CONTAINER, FACTORY, VALUE_EQUALS_COMPARER_TYPE>{factory, valueEqualsComparer};
        SimpleMappingTest_All_ (testschema);
    }
}

namespace {
    void Test2_SimpleBaseClassConversionTraitsConfusion_ ()
    {
        Debug::TraceContextBumper ctx{L"{}::Test2_SimpleBaseClassConversionTraitsConfusion_"};
        SortedMapping<int, float> xxxyy  = Concrete::SortedMapping_stdmap<int, float> ();
        Mapping<int, float>       xxxyy1 = Concrete::Mapping_stdmap<int, float> ();
    }
}

namespace {
    namespace Test4_MappingCTOROverloads_ {
        namespace xPrivate_ {
            struct A;
            struct B;
            struct A {
                A ()         = default;
                A (const A&) = default;
                A (const B&) {}
            };
            struct B {
                B () = default;
                B (const A&) {}
                B (const B&) = default;
            };
            using Common::KeyValuePair;
            using KEY_TYPE                = int;
            using VALUE_TYPE              = B;
            using CONTAINER_OF_PAIR_KEY_T = Mapping<int, A>;
            using T                       = KeyValuePair<KEY_TYPE, VALUE_TYPE>;
        }
        void DoIt ()
        {
            Debug::TraceContextBumper ctx{L"{}::Test4_MappingCTOROverloads_"};
            using namespace xPrivate_;
            Mapping<int, A> from;

            static_assert (Configuration::IsIterableOfT_v<Mapping<int, A>, KeyValuePair<int, A>>);
            static_assert (Configuration::IsIterableOfT_v<Mapping<int, B>, KeyValuePair<int, B>>);

            Mapping<int, B> to1;
            for (auto i : from) {
                to1.Add (i);
            }
            Mapping<int, B> to2{from};
        }
    }
}

namespace {
    namespace ExampleCTORS_Test_5_ {
        void DoTest ()
        {
            Debug::TraceContextBumper ctx{L"{}::ExampleCTORS_Test_5_"};
            // From Mapping<> CTOR docs
            Collection<pair<int, int>> c;
            std::map<int, int>         m;

            Mapping<int, int> m1 = {pair<int, int>{1, 1}, pair<int, int>{2, 2}, pair<int, int>{3, 2}};
            Mapping<int, int> m2 = m1;
            Mapping<int, int> m3{m1};
            Mapping<int, int> m4{m1.begin (), m1.end ()};
            Mapping<int, int> m5{c};
            Mapping<int, int> m6{m};
            Mapping<int, int> m7{m.begin (), m.end ()};
            Mapping<int, int> m8{move (m1)};
            Mapping<int, int> m9{Common::DeclareEqualsComparer ([] (int l, int r) { return l == r; })};
        }
    }
}

namespace {
    namespace Where_Test_6_ {
        void DoAll ()
        {
            Mapping<int, int> m{KeyValuePair<int, int>{1, 3}, KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{4, 5}, KeyValuePair<int, int>{5, 7}};
            VerifyTestResult ((m.Where ([] (const KeyValuePair<int, int>& value) { return Math::IsPrime (value.fKey); }) == Mapping<int, int>{KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{5, 7}}));
            VerifyTestResult ((m.Where ([] (int key) { return Math::IsPrime (key); }) == Mapping<int, int>{KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{5, 7}}));
        }
    }
}

namespace {
    namespace WithKeys_Test_7_ {
        void DoAll ()
        {
            Mapping<int, int> m{KeyValuePair<int, int>{1, 3}, KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{4, 5}, KeyValuePair<int, int>{5, 7}};
            VerifyTestResult ((m.WithKeys (initializer_list<int>{2, 5}) == Mapping<int, int>{KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{5, 7}}));
        }
    }
}

namespace {
    namespace ClearBug_Test_8_ {
        void DoAll ()
        {
            // https://stroika.atlassian.net/browse/STK-541
            Mapping<int, int> m{KeyValuePair<int, int>{1, 3}, KeyValuePair<int, int>{2, 4}, KeyValuePair<int, int>{3, 5}, KeyValuePair<int, int>{4, 5}, KeyValuePair<int, int>{5, 7}};
            Mapping<int, int> mm{move (m)};
            m.clear ();
        }
    }
}

namespace {
    namespace AddVsAddIf_Test_9_ {
        void DoAll ()
        {
            {
                Mapping<int, int> m;
                m.Add (1,2);
                VerifyTestResult (m[1] == 2);
                m.Add (1,3);
                VerifyTestResult (m[1] == 3);
                VerifyTestResult (not m.AddIf (1,4));
                VerifyTestResult (m[1] == 3);
                VerifyTestResult (m.AddIf (2,3));
                VerifyTestResult (m[2] == 3);
            }
        }
    }
}

namespace {
    void DoRegressionTests_ ()
    {
        struct MySimpleClassWithoutComparisonOperators_ComparerWithEquals_ : Common::ComparisonRelationDeclaration<Common::ComparisonRelationType::eEquals> {
            using value_type = SimpleClassWithoutComparisonOperators;
            bool operator() (const value_type& v1, const value_type& v2) const
            {
                return v1.GetValue () == v2.GetValue ();
            }
        };

        DoTestForConcreteContainer_<Mapping<size_t, size_t>> ();
        DoTestForConcreteContainer_<Mapping<SimpleClass, SimpleClass>> ();
        DoTestForConcreteContainer_<Mapping<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators>> (
            [] () { return Mapping<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators> (MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{}); },
            MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{});

        DoTestForConcreteContainer_<Mapping_Array<size_t, size_t>> ();
        DoTestForConcreteContainer_<Mapping_Array<SimpleClass, SimpleClass>> ();
        DoTestForConcreteContainer_<Mapping_Array<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators>> (
            [] () { return Mapping_Array<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators> (MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{}); },
            MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{});

        DoTestForConcreteContainer_<Mapping_LinkedList<size_t, size_t>> ();
        DoTestForConcreteContainer_<Mapping_LinkedList<SimpleClass, SimpleClass>> ();
        // DoTestForConcreteContainer_AllTestsWhichDontRequireComparer_For_Type_<Mapping_LinkedList<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators_MappingTRAITS>> ();
        DoTestForConcreteContainer_<Mapping_LinkedList<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators>> (
            [] () { return Mapping_LinkedList<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators> (MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{}); },
            MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{});

        DoTestForConcreteContainer_<Mapping_stdmap<size_t, size_t>> ();
        DoTestForConcreteContainer_<Mapping_stdmap<SimpleClass, SimpleClass>> ();
        {
            struct MySimpleClassWithoutComparisonOperators_ComparerWithLess_ : Common::ComparisonRelationDeclaration<Common::ComparisonRelationType::eStrictInOrder> {
                using value_type = SimpleClassWithoutComparisonOperators;
                bool operator() (const value_type& v1, const value_type& v2) const
                {
                    return v1.GetValue () < v2.GetValue ();
                }
            };
            DoTestForConcreteContainer_<Mapping_stdmap<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators>> (
                [] () { return Mapping_stdmap<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators> (MySimpleClassWithoutComparisonOperators_ComparerWithLess_{}); },
                //Common::mkEqualsComparerAdapter (MySimpleClassWithoutComparisonOperators_ComparerWithLess_{})
                MySimpleClassWithoutComparisonOperators_ComparerWithEquals_{});
        }

        Test2_SimpleBaseClassConversionTraitsConfusion_ ();

        //Test3_SimpleMappingTest_WhichRequiresExplcitValueComparer ();

        Test4_MappingCTOROverloads_::DoIt ();

        ExampleCTORS_Test_5_::DoTest ();

        Where_Test_6_::DoAll ();
        WithKeys_Test_7_::DoAll ();
        ClearBug_Test_8_::DoAll ();
        AddVsAddIf_Test_9_::DoAll ();
    }
}

int main ([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[])
{
    Stroika::TestHarness::Setup ();
    return Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
}
<|endoftext|>