#include "AbstractContext.h"
#include "context/Contexts.h"
#include "context/VariableDefinition.h"
#include "action/BasicActionDefinition.h"
#include "context/VariableData.h"
#include "context/FunctionData.h"
#include "context/EventData.h"
#include "context/ContextException.h"
#include "context/ContextSecurityException.h"
#include "context/ContextRuntimeException.h"
#include "context/CompatibilityConverter.h"
#include "context/VariableGetter.h"
#include "context/VariableStatus.h"
#include "context/ContextVisitor.h"
#include "context/FunctionDefinition.h"
#include "context/FunctionImplementation.h"
#include "security/NullPermissionChecker.h"
#include "event/ContextEventListener.h"
#include "datatable/DataTable.h"
#include "datatable/DataRecord.h"
#include "datatable/FieldFormat.h"
#include "datatable/DataTableReplication.h"
#include "datatable/DataTableConversion.h"
#include "datatable/ValidationException.h"
#include "datatable/encoding/ClassicEncodingSettings.h"
#include "IllegalStateException.h"
#include "IllegalArgumentException.h"
#include "Cres.h"
#include "context/ActionConstants.h"
#include "security/DefaultPermissionChecker.h"
#include "event/EventLevel.h"
#include "event/PersistenceOptions.h"
#include "event/EventProcessingRule.h"
#include "context/FireChangeEventRequestController.h"
#include "context/VariableSetter.h"
#include "action/TreeMask.h"
#include "util/Icons.h"
#include "util/StringUtils.h"
#include "util/MessageFormat.h"
#include "util/simpleobject/AgBoolean.h"
#include "event/EventUtils.h"
#include "event/EventLevel.h"
#include "event/Enrichment.h"
#include "expression/Evaluator.h"
#include "util/Pointers.h"

using namespace AbstractContextStrings;

TableFormatPtr AbstractContext::initVARIABLE_DEFINITION_FORMAT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());
    t->addField("<" + FIELD_VD_NAME + "><S>");
    t->addField("<" + FIELD_VD_FORMAT + "><S><F=N>");
    t->addField("<" + FIELD_VD_DESCRIPTION + "><S><F=N>");
    t->addField("<" + FIELD_VD_READABLE + "><B>");
    t->addField("<" + FIELD_VD_WRITABLE + "><B>");
    t->addField("<" + FIELD_VD_HELP + "><S><F=N>");
    t->addField("<" + FIELD_VD_GROUP + "><S><F=N>");
    t->addField("<" + FIELD_VD_ICON_ID + "><S><F=N>");
    t->addField("<" + FIELD_VD_HELP_ID + "><S><F=N>");
    t->addField("<" + FIELD_VD_CACHE_TIME + "><L><F=N>");
    return t;
}

TableFormatPtr AbstractContext::initEF_VARIABLE_ADDED()
{
    TableFormatPtr t = initVARIABLE_DEFINITION_FORMAT();
    t->setMinRecords(1);
    t->setMaxRecords(1);
    return t;
}

TableFormatPtr AbstractContext::initFUNCTION_DEFINITION_FORMAT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());
    t->addField("<" + FIELD_FD_NAME + "><S>");
    t->addField("<" + FIELD_FD_INPUTFORMAT + "><S><F=N>");
    t->addField("<" + FIELD_FD_OUTPUTFORMAT + "><S><F=N>");
    t->addField("<" + FIELD_FD_DESCRIPTION + "><S><F=N>");
    t->addField("<" + FIELD_FD_HELP + "><S><F=N>");
    t->addField("<" + FIELD_FD_GROUP + "><S><F=N>");
    t->addField("<" + FIELD_FD_ICON_ID + "><S><F=N>");
    return t;
}

TableFormatPtr AbstractContext::initEF_FUNCTION_ADDED()
{
    TableFormatPtr t = initFUNCTION_DEFINITION_FORMAT();
    t->setMinRecords(1);
    t->setMaxRecords(1);
    return t;
}

TableFormatPtr AbstractContext::initEVENT_DEFINITION_FORMAT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());
    t->addField("<" + FIELD_ED_NAME + "><S>");
    t->addField("<" + FIELD_ED_FORMAT + "><S><F=N>");
    t->addField("<" + FIELD_ED_DESCRIPTION + "><S><F=N>");
    t->addField("<" + FIELD_ED_HELP + "><S><F=N>");
    t->addField("<" + FIELD_ED_LEVEL + "><I>");
    t->addField("<" + FIELD_ED_GROUP + "><S><F=N>");
    t->addField("<" + FIELD_ED_ICON_ID + "><S><F=N>");
    return t;
}

TableFormatPtr AbstractContext::initEF_EVENT_ADDED()
{
    TableFormatPtr t = initEVENT_DEFINITION_FORMAT();
    t->setMinRecords(1);
    t->setMaxRecords(1);
    return t;
}

TableFormatPtr AbstractContext::initVFT_CHILDREN()
{
    FieldFormat *ff = FieldFormat::create("<" + VF_CHILDREN_NAME + "><S>");
    //here no need delete object, because new TableFormat object owner
    return ff->wrap();
}

TableFormatPtr AbstractContext::initINFO_DEFINITION_FORMAT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat(1, 1));
    t->addField("<" + VF_INFO_DESCRIPTION + "><S><F=N><D=" + Cres::get()->getString("description") + ">");
    t->addField("<" + VF_INFO_TYPE + "><S><D=" + Cres::get()->getString("type") + ">");
    t->addField("<" + VF_INFO_GROUP + "><S><F=N><D=" + Cres::get()->getString("group") + ">");
    t->addField("<" + VF_INFO_ICON + "><S><F=N><D=" + Cres::get()->getString("conIconId") + ">");
    t->addField("<" + VF_INFO_LOCAL_ROOT + "><S><D=" + Cres::get()->getString("conLocalRoot") + ">");
    t->addField("<" + VF_INFO_PEER_ROOT + "><S><F=N><D=" + Cres::get()->getString("conRemoteRoot") + ">");
    t->addField("<" + VF_INFO_PEER_PRIMARY_ROOT + "><S><F=N><D=" + Cres::get()->getString("conRemoteRoot") + ">");
    t->addField("<" + VF_INFO_REMOTE_ROOT + "><S><F=N><D=" + Cres::get()->getString("conRemoteRoot") + ">");
    t->addField("<" + VF_INFO_REMOTE_PATH + "><S><D=" + Cres::get()->getString("conRemotePath") + ">");
    t->addField("<" + VF_INFO_REMOTE_PRIMARY_ROOT + "><S><F=N><D=" + Cres::get()->getString("conRemotePrimaryRoot") + ">");
    t->addField("<" + VF_INFO_MAPPED + "><B><D=" + Cres::get()->getString("conMapped") + ">");
    return t;
}

TableFormatPtr AbstractContext::initACTION_DEF_FORMAT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());
    t->addField("<" + ActionConstants::FIELD_AD_NAME + "><S>");
    t->addField("<" + ActionConstants::FIELD_AD_DESCRIPTION + "><S><F=N>");
    t->addField("<" + ActionConstants::FIELD_AD_HELP + "><S><F=N>");
    t->addField("<" + ActionConstants::FIELD_AD_ACCELERATOR + "><S><F=N>");
    t->addField("<" + ActionConstants::FIELD_AD_DROP_SOURCES + "><T><F=N>");
    t->addField("<" + ActionConstants::FIELD_AD_HIDDEN + "><B>");
    t->addField("<" + ActionConstants::FIELD_AD_ENABLED + "><B>");
    t->addField("<" + ActionConstants::FIELD_AD_ICON_ID + "><S><F=N>");
    t->addField("<" + ActionConstants::FIELD_AD_GROUP + "><S><F=N>");
    t->addField("<" + ActionConstants::FIELD_AD_EXECUTION_GROUP + "><S><F=N>");
    t->addField("<" + ActionConstants::FIELD_AD_DEFAULT + "><B>");
    return t;
}

TableFormatPtr AbstractContext::initRESOURCE_MASKS_FORMAT()
{
    FieldFormat* ff = FieldFormat::create("<" + ActionConstants::FIELD_AD_RESOURCE_MASKS_RESOURCE_MASK + "><S><F=N>");
    return ff->wrap();
}

TableFormatPtr AbstractContext::initFIFT_GET_COPY_DATA()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat(1, 1));
    t->addField("<" + FIF_COPY_DATA_GROUP + "><S><F=N>");
    t->addField("<" + FIF_COPY_DATA_RECIPIENTS + "><T><F=N>");
    return t;
}

TableFormatPtr AbstractContext::initFIFT_GET_COPY_DATA_RECIPIENTS()
{
    FieldFormat* ff = FieldFormat::create("<" + FIF_COPY_DATA_RECIPIENTS_RECIPIENT + "><S>");
    return ff->wrap();
}

TableFormatPtr AbstractContext::initREPLICATE_INPUT_FORMAT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());
    t->addField("<" + FOF_COPY_DATA_NAME + "><S><F=RHK>");
    t->addField("<" + FOF_COPY_DATA_DESCRIPTION + "><S><F=R><D=" + Cres::get()->getString("variable") + ">");
    t->addField("<" + FOF_COPY_DATA_REPLICATE + "><B><A=0><D=" + Cres::get()->getString("replicate") + ">");
    t->addField("<" + FOF_COPY_DATA_FIELDS + "><T><D=" + Cres::get()->getString("fields") + ">");
    t->addField("<" + FOF_COPY_DATA_VALUE + "><T><D=" + Cres::get()->getString("value") + ">");
    return t;
}

TableFormatPtr AbstractContext::initFIFT_REPLICATE_FIELDS()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());
    t->addField("<" + FIF_REPLICATE_FIELDS_NAME + "><S><F=RHK>");
    t->addField("<" + FIF_REPLICATE_FIELDS_DESCRIPTION + "><S><F=R><D=" + Cres::get()->getString("field") + ">");
    t->addField("<" + FIF_REPLICATE_FIELDS_REPLICATE + "><B><A=1><D=" + Cres::get()->getString("replicate") + ">");
    t->setNamingExpression("print({}, '{" + FIF_REPLICATE_FIELDS_REPLICATE + "} ? {" + FIF_REPLICATE_FIELDS_DESCRIPTION + "} : null', ', ')");
    return t;
}

TableFormatPtr AbstractContext::initREPLICATE_OUTPUT_FORMAT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());
    t->addField("<" + FIELD_REPLICATE_VARIABLE + "><S><D=" + Cres::get()->getString("variable") + ">");
    t->addField("<" + FIELD_REPLICATE_SUCCESSFUL + "><B><D=" + Cres::get()->getString("successful") + ">");
    t->addField("<" + FIELD_REPLICATE_ERRORS + "><S><D=" + Cres::get()->getString("errors") + ">");
    return t;
}

TableFormatPtr AbstractContext::initREPLICATE_TO_CHILDREN_OUTPUT_FORMAT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());
    t->addField("<" + FIELD_REPLICATE_CONTEXT + "><S><D=" + Cres::get()->getString("context") + ">");
    t->addField("<" + FIELD_REPLICATE_VARIABLE + "><S><D=" + Cres::get()->getString("variable") + ">");
    t->addField("<" + FIELD_REPLICATE_SUCCESSFUL + "><B><D=" + Cres::get()->getString("successful") + ">");
    t->addField("<" + FIELD_REPLICATE_ERRORS + "><S><D=" + Cres::get()->getString("errors") + ">");
    return t;
}

TableFormatPtr AbstractContext::initEF_UPDATED()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat(1, 1));
    t->addField("<" + EF_UPDATED_VARIABLE + "><S>");
    t->addField("<" + EF_UPDATED_VALUE + "><T>");
    t->addField("<" + EF_UPDATED_USER + "><S><F=N>");
    return t;
}

TableFormatPtr AbstractContext::initEF_CHANGE()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat(1, 1));
    t->addField("<" + EF_CHANGE_VARIABLE + "><S>");
    t->addField("<" + EF_CHANGE_VALUE + "><T><F=N>");
    t->addField("<" + EF_CHANGE_DATA + "><S><F=N>");
    return t;
}

VariableDefinitionPtr AbstractContext::initVD_INFO()
{
    VariableDefinitionPtr v = VariableDefinitionPtr(new VariableDefinition(V_INFO, INFO_DEFINITION_FORMAT, true, false, Cres::get()->getString("conContextProps"), ContextUtils::GROUP_SYSTEM()));
    v->setHidden(true);
    v->setReadPermissions(DefaultPermissionChecker::getNullPermissions());
    return v;
}

VariableDefinitionPtr AbstractContext::initVD_FUNCTIONS()
{
    VariableDefinitionPtr v = VariableDefinitionPtr(new VariableDefinition(V_FUNCTIONS, FUNCTION_DEFINITION_FORMAT, true, false, Cres::get()->getString("conFuncList")));
    v->setHidden(true);
    v->setReadPermissions(DefaultPermissionChecker::getNullPermissions());
    return v;
}

VariableDefinitionPtr AbstractContext::initVD_EVENTS()
{
    VariableDefinitionPtr v = VariableDefinitionPtr(new VariableDefinition(V_EVENTS, EVENT_DEFINITION_FORMAT, true, false, Cres::get()->getString("conEvtList")));
    v->setHidden(true);
    v->setReadPermissions(DefaultPermissionChecker::getNullPermissions());
    return v;
}

VariableDefinitionPtr AbstractContext::initVD_ACTIONS()
{
    VariableDefinitionPtr v = VariableDefinitionPtr(new VariableDefinition(V_ACTIONS, ACTION_DEF_FORMAT, true, false, Cres::get()->getString("conActionList")));
    v->setHidden(true);
    v->setReadPermissions(DefaultPermissionChecker::getNullPermissions());
    return v;
}

VariableDefinitionPtr AbstractContext::initVD_CHILDREN()
{
    VariableDefinitionPtr v = VariableDefinitionPtr(new VariableDefinition(V_CHILDREN, VFT_CHILDREN, true, false, Cres::get()->getString("conChildList")));
    v->setHidden(true);
    v->setReadPermissions(DefaultPermissionChecker::getNullPermissions());
    return v;
}


VariableDefinitionPtr AbstractContext::initVD_VARIABLES()
{
    VariableDefinitionPtr v = VariableDefinitionPtr(new VariableDefinition(V_VARIABLES, VARIABLE_DEFINITION_FORMAT, true, false, Cres::get()->getString("conVarList")));
    v->setHidden(true);
    v->setReadPermissions(DefaultPermissionChecker::getNullPermissions());
    return v;
}

FunctionDefinitionPtr AbstractContext::initFD_GET_COPY_DATA()
{
    FunctionDefinitionPtr f = FunctionDefinitionPtr(new FunctionDefinition(F_GET_COPY_DATA, FIFT_GET_COPY_DATA, REPLICATE_INPUT_FORMAT));
    f->setHidden(true);
    return f;
}

FunctionDefinitionPtr AbstractContext::initFD_COPY()
{
    FunctionDefinitionPtr f = FunctionDefinitionPtr(new FunctionDefinition(F_COPY, REPLICATE_INPUT_FORMAT, REPLICATE_OUTPUT_FORMAT, Cres::get()->getString("conCopyProperties")));
    f->setHidden(true);
    return f;
}

FunctionDefinitionPtr AbstractContext::initFD_COPY_TO_CHILDREN()
{
    FunctionDefinitionPtr f = FunctionDefinitionPtr(new FunctionDefinition(F_COPY_TO_CHILDREN, REPLICATE_INPUT_FORMAT, REPLICATE_TO_CHILDREN_OUTPUT_FORMAT, Cres::get()->getString(
                                                                               "conCopyToChildren")));
    f->setHidden(true);
    return f;
}

EventDefinitionPtr AbstractContext::initED_INFO()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_INFO, EFT_INFO, Cres::get()->getString("info"), ContextUtils::GROUP_DEFAULT()));
    e->setLevel(EventLevel::INFO());
    e->setIconId(Icons::EVT_INFO);
    e->getPersistenceOptions()->setDedicatedTablePreferred(true);
    return e;
}

EventDefinitionPtr AbstractContext::initED_CHILD_ADDED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_CHILD_ADDED, EFT_CHILD_ADDED, Cres::get()->getString("conChildAdded"), ContextUtils::GROUP_SYSTEM()));
    e->setSynchronous(true);
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_CHILD_REMOVED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_CHILD_REMOVED, EFT_CHILD_REMOVED, Cres::get()->getString("conChildRemoved"), ContextUtils::GROUP_SYSTEM()));
    e->setSynchronous(true);
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_VARIABLE_ADDED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_VARIABLE_ADDED, EF_VARIABLE_ADDED, Cres::get()->getString("conVarAdded"), ContextUtils::GROUP_SYSTEM()));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_VARIABLE_REMOVED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_VARIABLE_REMOVED, EFT_VARIABLE_REMOVED, Cres::get()->getString("conVarRemoved"), ContextUtils::GROUP_SYSTEM()));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_FUNCTION_ADDED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_FUNCTION_ADDED, EF_FUNCTION_ADDED, Cres::get()->getString("conFuncAdded"), ContextUtils::GROUP_SYSTEM()));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_FUNCTION_REMOVED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_FUNCTION_REMOVED, EFT_FUNCTION_REMOVED, Cres::get()->getString("conFuncRemoved"), ContextUtils::GROUP_SYSTEM()));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_EVENT_ADDED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_EVENT_ADDED, EF_EVENT_ADDED, Cres::get()->getString("conEvtAdded"), ContextUtils::GROUP_SYSTEM()));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_EVENT_REMOVED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_EVENT_REMOVED, EFT_EVENT_REMOVED, Cres::get()->getString("conEvtRemoved"), ContextUtils::GROUP_SYSTEM()));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_ACTION_ADDED()
{
    TableFormatPtr tc = TableFormatPtr(ACTION_DEF_FORMAT->clone());
    tc->setMinRecords(1);
    tc->setMaxRecords(1);
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_ACTION_ADDED, tc, Cres::get()->getString("conActionAdded")));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_ACTION_REMOVED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_ACTION_REMOVED, EFT_ACTION_REMOVED, Cres::get()->getString("conActionRemoved")));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_ACTION_STATE_CHANGED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_ACTION_STATE_CHANGED, ACTION_DEF_FORMAT, Cres::get()->getString("conActionStateChanged")));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_INFO_CHANGED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_INFO_CHANGED, INFO_DEFINITION_FORMAT, Cres::get()->getString("conInfoChanged"), ContextUtils::GROUP_SYSTEM()));
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

EventDefinitionPtr AbstractContext::initED_UPDATED()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_UPDATED, EF_UPDATED, Cres::get()->getString("conUpdated"), ContextUtils::GROUP_SYSTEM()));
    e->setHidden(true);
    return e;
}

EventDefinitionPtr AbstractContext::initED_CHANGE()
{
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_CHANGE, EF_CHANGE, Cres::get()->getString("change"), ContextUtils::GROUP_SYSTEM()));
    e->setHidden(true);
    e->getPersistenceOptions()->setDedicatedTablePreferred(true);
    return e;
}

EventDefinitionPtr AbstractContext::initED_DESTROYED()
{
    TableFormatPtr EMPTY_FORMAT = TableFormatPtr(new TableFormat(0, 0));
    EventDefinitionPtr e = EventDefinitionPtr(new EventDefinition(E_DESTROYED, EMPTY_FORMAT, Cres::get()->getString("conDestroyedPermanently"), ContextUtils::GROUP_SYSTEM()));
    e->setSynchronous(true);
    e->setHidden(true);
    e->setPermissions(DefaultPermissionChecker::getNullPermissions());
    return e;
}

TableFormatPtr AbstractContext::initVFT_VARIABLE_STATUSES()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());
    t->addField("<" + VF_VARIABLE_STATUSES_NAME + "><S>");
    t->addField("<" + VF_VARIABLE_STATUSES_STATUS + "><S><F=N>");
    t->addField("<" + VF_VARIABLE_STATUSES_COMMENT + "><S><F=N>");
    return t;
}

TableFormatPtr AbstractContext::VARIABLE_DEFINITION_FORMAT = AbstractContext::initVARIABLE_DEFINITION_FORMAT();
TableFormatPtr AbstractContext::EF_VARIABLE_ADDED = AbstractContext::initEF_VARIABLE_ADDED();
TableFormatPtr AbstractContext::FUNCTION_DEFINITION_FORMAT = AbstractContext::initFUNCTION_DEFINITION_FORMAT();
TableFormatPtr AbstractContext::EF_FUNCTION_ADDED = AbstractContext::initEF_FUNCTION_ADDED();
TableFormatPtr AbstractContext::EVENT_DEFINITION_FORMAT = AbstractContext::initEVENT_DEFINITION_FORMAT();
TableFormatPtr AbstractContext::EF_EVENT_ADDED = AbstractContext::initEF_EVENT_ADDED();
TableFormatPtr AbstractContext::VFT_CHILDREN = AbstractContext::initVFT_CHILDREN();
TableFormatPtr AbstractContext::INFO_DEFINITION_FORMAT = AbstractContext::initINFO_DEFINITION_FORMAT();
TableFormatPtr AbstractContext::ACTION_DEF_FORMAT = AbstractContext::initACTION_DEF_FORMAT();
TableFormatPtr AbstractContext::RESOURCE_MASKS_FORMAT = AbstractContext::initRESOURCE_MASKS_FORMAT();
TableFormatPtr AbstractContext::FIFT_GET_COPY_DATA = AbstractContext::initFIFT_GET_COPY_DATA();
TableFormatPtr AbstractContext::FIFT_GET_COPY_DATA_RECIPIENTS = AbstractContext::initFIFT_GET_COPY_DATA_RECIPIENTS();
TableFormatPtr AbstractContext::REPLICATE_INPUT_FORMAT = AbstractContext::initREPLICATE_INPUT_FORMAT();
TableFormatPtr AbstractContext::FIFT_REPLICATE_FIELDS = AbstractContext::initFIFT_REPLICATE_FIELDS();
TableFormatPtr AbstractContext::REPLICATE_OUTPUT_FORMAT = AbstractContext::initREPLICATE_OUTPUT_FORMAT();
TableFormatPtr AbstractContext::REPLICATE_TO_CHILDREN_OUTPUT_FORMAT = AbstractContext::initREPLICATE_TO_CHILDREN_OUTPUT_FORMAT();
TableFormatPtr AbstractContext::EF_UPDATED = AbstractContext::initEF_UPDATED();
TableFormatPtr AbstractContext::EF_CHANGE = AbstractContext::initEF_CHANGE();
TableFormatPtr AbstractContext::EFT_INFO = TableFormatPtr(new TableFormat(1, 1, "<" + EF_INFO_INFO + "><S><D=" + Cres::get()->getString("info") + ">"));
TableFormatPtr AbstractContext::EFT_VARIABLE_REMOVED = TableFormatPtr(new TableFormat(1, 1, "<" + EF_VARIABLE_REMOVED_NAME + "><S>"));
TableFormatPtr AbstractContext::EFT_EVENT_REMOVED = TableFormatPtr(new TableFormat(1, 1, "<" + EF_EVENT_REMOVED_NAME + "><S>"));
TableFormatPtr AbstractContext::EFT_FUNCTION_REMOVED = TableFormatPtr(new TableFormat(1, 1, "<" + EF_FUNCTION_REMOVED_NAME + "><S>"));
TableFormatPtr AbstractContext::EFT_CHILD_REMOVED = TableFormatPtr(new TableFormat(1, 1, "<" + EF_CHILD_REMOVED_CHILD + "><S>"));
TableFormatPtr AbstractContext::EFT_CHILD_ADDED = TableFormatPtr(new TableFormat(1, 1, "<" + EF_CHILD_ADDED_CHILD + "><S>"));
TableFormatPtr AbstractContext::EFT_ACTION_REMOVED = TableFormatPtr(new TableFormat(1, 1, "<" + EF_ACTION_REMOVED_NAME + "><S>"));

VariableDefinitionPtr AbstractContext::VD_INFO = AbstractContext::initVD_INFO();
VariableDefinitionPtr AbstractContext::VD_FUNCTIONS = AbstractContext::initVD_FUNCTIONS();
VariableDefinitionPtr AbstractContext::VD_EVENTS = AbstractContext::initVD_EVENTS();
VariableDefinitionPtr AbstractContext::VD_ACTIONS = AbstractContext::initVD_ACTIONS();
VariableDefinitionPtr AbstractContext::VD_CHILDREN = AbstractContext::initVD_CHILDREN();
VariableDefinitionPtr AbstractContext::VD_VARIABLES = AbstractContext::initVD_VARIABLES();

FunctionDefinitionPtr AbstractContext::FD_GET_COPY_DATA = AbstractContext::initFD_GET_COPY_DATA();
FunctionDefinitionPtr AbstractContext::FD_COPY = AbstractContext::initFD_COPY();
FunctionDefinitionPtr AbstractContext::FD_COPY_TO_CHILDREN = AbstractContext::initFD_COPY_TO_CHILDREN();

EventDefinitionPtr AbstractContext::ED_INFO = AbstractContext::initED_INFO();
EventDefinitionPtr AbstractContext::ED_CHILD_ADDED = AbstractContext::initED_CHILD_ADDED();
EventDefinitionPtr AbstractContext::ED_CHILD_REMOVED = AbstractContext::initED_CHILD_REMOVED();
EventDefinitionPtr AbstractContext::ED_VARIABLE_ADDED = AbstractContext::initED_VARIABLE_ADDED();
EventDefinitionPtr AbstractContext::ED_VARIABLE_REMOVED = AbstractContext::initED_VARIABLE_REMOVED();
EventDefinitionPtr AbstractContext::ED_FUNCTION_ADDED = AbstractContext::initED_FUNCTION_ADDED();
EventDefinitionPtr AbstractContext::ED_FUNCTION_REMOVED = AbstractContext::initED_FUNCTION_REMOVED();
EventDefinitionPtr AbstractContext::ED_EVENT_ADDED = AbstractContext::initED_EVENT_ADDED();
EventDefinitionPtr AbstractContext::ED_EVENT_REMOVED = AbstractContext::initED_EVENT_REMOVED();
EventDefinitionPtr AbstractContext::ED_ACTION_ADDED = AbstractContext::initED_ACTION_ADDED();
EventDefinitionPtr AbstractContext::ED_ACTION_REMOVED = AbstractContext::initED_ACTION_REMOVED();
EventDefinitionPtr AbstractContext::ED_ACTION_STATE_CHANGED = AbstractContext::initED_ACTION_STATE_CHANGED();
EventDefinitionPtr AbstractContext::ED_INFO_CHANGED = AbstractContext::initED_INFO_CHANGED();
EventDefinitionPtr AbstractContext::ED_UPDATED = AbstractContext::initED_UPDATED();
EventDefinitionPtr AbstractContext::ED_CHANGE = AbstractContext::initED_CHANGE();
EventDefinitionPtr AbstractContext::ED_DESTROYED = AbstractContext::initED_DESTROYED();
TableFormatPtr AbstractContext::VFT_VARIABLE_STATUSES = AbstractContext::initVFT_VARIABLE_STATUSES();

PermissionsPtr AbstractContext::DEFAULT_PERMISSIONS = DefaultPermissionChecker::getNullPermissions();


AbstractContext::AbstractContext(const AgString &name)
{
    contextManager = NULL;
    parent = NULL;
    setupComplete = false;
    started = false;
    permissionCheckingEnabled = true;
    valueCheckingEnabled = true;
    childrenConcurrencyEnabled = false;
    childrenSortingEnabled = true;
    fireUpdateEvents = true;
    variableStatusesUpdated = false;
    permissionChecker = PermissionCheckerPtr(new NullPermissionChecker());
    setName(name);
    functionData = new (std::map<AgString, FunctionDataPtr>);
    variableCounter = 0;
    functionCounter = 0;
    eventCounter = 0;
}

AbstractContext::~AbstractContext()
{
    for (std::vector<Context *>::iterator it = children.begin(); it != children.end(); ++it)
    {
        delete *it;
    }

    {
        boost::lock_guard<boost::mutex> lock(functionDataLock);
        for (std::map<AgString, FunctionDataPtr>::iterator it = functionData->begin(); it != functionData->end(); ++it)
        {
            FunctionDataPtr data;

            data = it->second;

            boost::recursive_mutex &mutex = data->getExecutionLock();
            boost::lock_guard<boost::recursive_mutex> lock(mutex);
        }

        functionData->clear();
        delete functionData;
    }
}

void AbstractContext::setup(ContextManager* contextManager)
{
    setContextManager(contextManager);
    setup();
}

void AbstractContext::setup()
{
    try
    {
        if(setupComplete)
        {
            return;
        }
        setupPermissions();
        setupMyself();
        setupComplete = true;
        setupChildren();
    }
    catch (AggreGateException ex)
    {
        throw ContextRuntimeException("Error setting up context" , ex.getDetails());
    }
}

void AbstractContext::setupPermissions()
{
}


void AbstractContext::setupMyself()
{
    addVariableDefinition(VD_INFO);
    addVariableDefinition(VD_VARIABLES);
    addVariableDefinition(VD_FUNCTIONS);
    addVariableDefinition(VD_EVENTS);
    addVariableDefinition(VD_ACTIONS);
    addVariableDefinition(VD_CHILDREN);
    addFunctionDefinition(FD_GET_COPY_DATA);
    addFunctionDefinition(FD_COPY);
    addFunctionDefinition(FD_COPY_TO_CHILDREN);
    addEventDefinition(ED_INFO);
    addEventDefinition(ED_CHILD_ADDED);
    addEventDefinition(ED_CHILD_REMOVED);
    addEventDefinition(ED_VARIABLE_ADDED);
    addEventDefinition(ED_VARIABLE_REMOVED);
    addEventDefinition(ED_FUNCTION_ADDED);
    addEventDefinition(ED_FUNCTION_REMOVED);
    addEventDefinition(ED_EVENT_ADDED);
    addEventDefinition(ED_EVENT_REMOVED);
    addEventDefinition(ED_ACTION_ADDED);
    addEventDefinition(ED_ACTION_REMOVED);
    addEventDefinition(ED_ACTION_STATE_CHANGED);
    addEventDefinition(ED_INFO_CHANGED);
    addEventDefinition(ED_UPDATED);
    addEventDefinition(getChangeEventDefinition());
    addEventDefinition(ED_DESTROYED);
}

void AbstractContext::setupChildren()
{
}

void AbstractContext::teardown()
{
}

void AbstractContext::start()
{
    std::list< boost::shared_ptr<Callable<int> > > tasks;

    boost::lock_guard<boost::mutex> lock(childrenLock);//readLock()
    {
        for (std::vector<Context *>::iterator child=children.begin(); child!=children.end(); ++child)
        {
            tasks.push_back( boost::shared_ptr<Callable<int> >(new StartContextTask(*child)) );
        }
    }

    executeTasks(tasks);

    started = true;
}


void AbstractContext::stop()
{
    std::list< boost::shared_ptr<Callable<int> > > tasks;

    boost::lock_guard<boost::mutex> lock(childrenLock);//readLock()
    {
        for (std::vector<Context* >::iterator child=children.begin(); child!=children.end(); ++child)
        {
            tasks.push_back( boost::shared_ptr<Callable<int> >(new StopContextTask(*child)) );
        }
    }

    executeTasks(tasks);

    started = false;
}

int AbstractContext::compareTo(Comparable *other)
{
    Context *context = dynamic_cast<Context *>(other);
    assert(context);

    if(getIndex().is_initialized() || context->getIndex().is_initialized())
    {
        int my = getIndex().is_initialized() ? *getIndex() : 0;
        int other = context->getIndex().is_initialized() ? *context->getIndex() : 0;

        AgInteger ag_my(my);
        AgInteger ag_other(other);
        return ag_other.compareTo(&ag_my);
    }
    else
    {
        return getName().compare(context->getName());
    }
}

std::list<Context*> AbstractContext::getChildren(CallerControllerPtr caller)
{
    if (!checkPermissions(getChildrenViewPermissions(), caller, this))
    {
        if(Log::CONTEXT_CHILDREN.isDebugEnabled())
        {
            LOG_CONTEXT_CHILDREN_DEBUG("Access to child '" + name.toUtf8() + "' denied in context '" + getPath().toUtf8() + "'");
        }
        return std::list<Context*>();
    }

    std::list<Context*> childList;
    for (std::vector<Context* >::iterator it = children.begin(); it != children.end(); ++it)
    {
        if (shouldSeeChild(caller, *it))
        {
            childList.push_back(*it);
        }
    }
    return childList;
}

bool AbstractContext::shouldSeeChild(CallerControllerPtr caller, Context* cur)
{
    return checkPermissions(cur->getPermissions(), caller, cur) || canSee(caller, cur);
}

bool AbstractContext::canSee(CallerControllerPtr caller, Context* con)
{
    if(!permissionCheckingEnabled)
    {
        return true;
    }
    return getPermissionChecker()->canSee(caller.get() != NULL ? caller->getPermissions() : PermissionsPtr(), con->getPath(), getContextManager());
}

std::list<Context*> AbstractContext::getChildren()
{
    return getChildren(CallerControllerPtr());
}

std::list<Context*>   AbstractContext::getVisibleChildren(CallerControllerPtr caller)
{
    return getChildren(caller);
}

std::list<Context*>   AbstractContext::getVisibleChildren()
{
    return getVisibleChildren(CallerControllerPtr());
}

bool AbstractContext::isMapped()
{
    return false;
}

std::list<Context*>  AbstractContext::getMappedChildren(CallerControllerPtr caller)
{
    return isMapped() ? getVisibleChildren(caller) : getChildren(caller);
}

std::list<Context*> AbstractContext::getMappedChildren()
{
    return getMappedChildren(CallerControllerPtr());
}

AgString AbstractContext::getName()
{
    return name;
}

AgString AbstractContext::getDescription()
{
    return description;
}

void AbstractContext::setDescription(const AgString & description)
{
    AgString old = this->description;
    this->description = description;

    if (!old.equals(&(this->description)))
    {
        contextInfoChanded();
    }
}

Context* AbstractContext::getParent()
{
    return parent;
}

bool AbstractContext::hasParent(Context * parentContext)
{
    Context* root = this;

    do
    {
        root = root->getParent();
        if (root == parentContext)
        {
            return true;
        }
    } while (root->getParent() != NULL);

    return false;
}

Context* AbstractContext::getRoot()
{
    Context* root = this;

    while (root->getParent() != NULL)
    {
        root = root->getParent();
    }

    return root;
}

Context* AbstractContext::get(const AgString & contextPath, CallerControllerPtr caller)
{
    AgString contextPath_ = contextPath;
    bool relative = ContextUtils::isRelative(contextPath_);

    if (relative)
    {
        contextPath_ = contextPath_.substr(1);
    }

    Context* cur = relative ? this : getRoot();

    if (contextPath_.empty())
    {
        return cur;
    }

    AgString lastName = getRoot()->getName();

    std::vector<AgString> names = StringUtils::split(contextPath_, ContextUtils::CONTEXT_NAME_SEPARATOR()[0]);
    for (std::vector<AgString>::iterator child = names.begin(); child != names.end(); ++child)
    {
        if (child->length() == 0)
        {
            return NULL;
        }

        if (cur == NULL)
        {
            break;
        }

        lastName = cur->getName();
        cur = cur->getChild(*child, caller);
    }
    if (cur == NULL)
    {
        LOG_CONTEXT_CHILDREN_DEBUG("Context '" + contextPath_.toUtf8() + "' not found in '" + getPath().toUtf8() + "', last found: '" + lastName.toUtf8() + "'");
    }
    return cur;
}

Context* AbstractContext::get(const AgString & contextName)
{
    return get(contextName, CallerControllerPtr());
}

PermissionsPtr AbstractContext::getPermissions()
{
    if (!permissionCheckingEnabled)
    {
        return DEFAULT_PERMISSIONS;
    }

    if (permissions.get() != NULL)
    {
        return permissions;
    }

    if (getParent() != NULL)
    {
        return getParent()->getPermissions();
    }
    return DEFAULT_PERMISSIONS;
}

void AbstractContext::setName(const AgString &name)
{
    if(!ContextUtils::isValidContextName(name))
    {
        throw IllegalArgumentException(Cres::get()->getString("conIllegalName") + name);
    }
    this->name = name;
}

void AbstractContext::setParent(Context* parent)
{
    this->parent = parent;
}

void AbstractContext::setPermissions(PermissionsPtr permissions)
{
    this->permissions = permissions;
}

void AbstractContext::setPermissionChecker(PermissionCheckerPtr permissionChecker)
{
    this->permissionChecker = permissionChecker;
}

void AbstractContext::setFireUpdateEvents(bool fireUpdateEvents)
{
    this->fireUpdateEvents = fireUpdateEvents;
}

bool AbstractContext::isFireUpdateEvents()
{
    return fireUpdateEvents;
}

void AbstractContext::setContextManager(ContextManager* contextManager)
{
    if ((this->contextManager != NULL) &&  (this->contextManager != contextManager))
    {
        throw IllegalStateException("Context manager already set");
    }
    this->contextManager = contextManager;
}

void AbstractContext::setChildrenViewPermissions(PermissionsPtr childrenViewPermissions)
{
    this->childrenViewPermissions = childrenViewPermissions;
}

void AbstractContext::setChildrenSortingEnabled(bool childrenSortingEnabled)
{
    this->childrenSortingEnabled = childrenSortingEnabled;
}

bool AbstractContext::isChildrenSortingEnabled()
{
    return childrenSortingEnabled;
}

void AbstractContext::setValueCheckingEnabled(bool valueCheckingEnabled)
{
    this->valueCheckingEnabled = valueCheckingEnabled;
}

bool AbstractContext::isChildrenConcurrencyEnabled()
{
    return childrenConcurrencyEnabled;
}

void AbstractContext::setChildrenConcurrencyEnabled(bool childrenConcurrencyEnabled)
{
    this->childrenConcurrencyEnabled = childrenConcurrencyEnabled;
}

void AbstractContext::checkPermissions(PermissionsPtr needPermissions, CallerControllerPtr caller)
{
    if(!checkPermissions(needPermissions, caller, this))
    {
        throw ContextSecurityException(MessageFormat::format(Cres::get()->getString("conAccessDenied"), getPath(), caller.get() != NULL ? caller->getPermissions()->toString() : "", needPermissions->toString()));
    }
}

bool AbstractContext::checkPermissions(PermissionsPtr needPermissions, CallerControllerPtr caller, Context* accessedContext)
{
    if(!permissionCheckingEnabled)
    {
        return true;
    }
    return getPermissionChecker()->has(caller, needPermissions, accessedContext);
}

void AbstractContext::addChild(Context *child)
{
    boost::optional<int> notInitInd;
    addChild(child, notInitInd);
}

void AbstractContext::addChild(Context* child, boost::optional<int>  index)
{
    struct compareContexts
    {
        inline bool operator() (Context* struct1, Context* struct2)
        {
            return struct1->compareTo(struct2) <= 0;
        }
    };

    //boost::posix_time::ptime startTime = boost::posix_time::microsec_clock::local_time();

    {
        boost::lock_guard<boost::mutex> lock(childrenLock);

        Context* existing = getChildWithoutCheckingPerms(child->getName());
        if (existing != NULL)
        {
            throw IllegalArgumentException(MessageFormat::format(Cres::get()->getString("conChildExists"), child->getName(), getPath()));
        }

        if (index.is_initialized())
        {
            if (childrenSortingEnabled)
            {
                throw IllegalStateException("Cannot add child with pre-defined index as children sorting is enabled");
            }

            children.insert(children.begin() + (*index), child);
        }
        else
        {
            children.push_back(child);
        }

        childrenMap[child->getName()] = child;

        if (childrenSortingEnabled)
        {
            std::sort(children.begin(), children.end());
        }
    }

    try
    {
        child->setParent(this);
        child->setup(getContextManager());

        if (setupComplete && fireUpdateEvents)
        {
            fireEvent(E_CHILD_ADDED, child->getName());
        }

        if (getContextManager() != NULL)
        {
            // If a child added already has own children, contextAdded() won't be called for them
            getContextManager()->contextAdded(child);
        }
    }
    catch (AggreGateException ex)
    {
        boost::lock_guard<boost::mutex> lock(childrenLock);

        std::map<AgString, Context *>::iterator it = childrenMap.find(child->getName());
        if (it != childrenMap.end())
        {
            childrenMap.erase(it);

            std::vector<Context*>::iterator itc = std::find(children.begin(), children.end(), child);
            if (itc != children.end())
            {
                children.erase(itc);
            }
        }

        throw ContextRuntimeException("Error adding child '" + child->toString() + "' to context '"
                                      + toString() + "': " + ex.getMessage(), ex.getDetails());
    }

    //boost::posix_time::time_duration dur = boost::posix_time::microsec_clock::local_time() - startTime;
    //AGDEBUG(DBG_DEBUG, "CONTEXT_CHILDREN", "Added child '" + child->getName() + "' to '" + getPath() + "' in " + AgString::fromInt64(dur.total_milliseconds()) + " ms");
}

void AbstractContext::removeFromParent()
{
    if (getParent() != NULL)
    {
        getParent()->removeChild(this);
        setParent(NULL);
    }
    else
    {
        if(Log::CONTEXT_CHILDREN.isDebugEnabled())
        {
            LOG_CONTEXT_CHILDREN_DEBUG("Can't remove context '" + getPath().toUtf8() + "' from its parent: no parent context was set");
        }
    }
}

void AbstractContext::destroy(bool moving)
{
    if (!moving)
    {
        stop();
        destroyChildren(false);
    }

    if (fireUpdateEvents)
    {
        EventDefinitionPtr ed = getEventDefinition(E_DESTROYED);
        if(ed.get() != NULL)
        {
            fireEvent(ed->getName());
        }
    }
    {
        boost::lock_guard<boost::mutex> lock(eventDataLock);

        for (std::map<AgString, EventDataPtr>::iterator ed = eventData.begin(); ed != eventData.end(); ++ed)
        {
            if(Log::CONTEXT_CHILDREN.isDebugEnabled())
            {
                LOG_CONTEXT_CHILDREN_DEBUG("Removing all listeners of event '" + ed->second->getDefinition()->getName().toUtf8() + "'");
            }
            ed->second->clearListeners();
        }
    }

    removeFromParent();
}

void AbstractContext::destroyChildren(bool moving)
{
    boost::lock_guard<boost::mutex> lock(childrenLock);

    for (std::vector<Context*>::iterator child = children.begin(); child != children.end(); ++child)
    {
        (*child)->destroy(moving);
    }
}

void AbstractContext::removeChild(Context* child)
{
    child->teardown();

    boost::lock_guard<boost::mutex> lock(childrenLock);

    std::vector<Context*>::iterator it = std::find(children.begin(), children.end(), child);
    if (it != children.end())
    {
        if (getContextManager() != NULL)
        {
            NewContextVisitorPtr cvp = NewContextVisitorPtr(new NewContextVisitor(this, child)); // fix gcc error: template argument for 'template<class T> template<class Y> boost::shared_ptr<T>::shared_ptr(Y*)' uses local type 'AbstractContext::removeChild(Context*)::NewContextVisitor'
            try
            {
                child->accept(cvp);
            }
            catch (ContextException ex)
            {
                throw ContextRuntimeException(ex.getMessage(), ex.getDetails());
            }
        }

        std::map<AgString,Context *>::iterator im = childrenMap.find(child->getName());
        if (im != childrenMap.end())
        {
            childrenMap.erase(im);
        }

        delete *it;
        children.erase(it);

        if (setupComplete && fireUpdateEvents)
        {
            fireEvent(E_CHILD_REMOVED, child->getName());
        }
    }

}

void AbstractContext::removeChild(const AgString & name)
{
    Context* con = getChildWithoutCheckingPerms(name);

    if (con != NULL)
    {
        removeChild(con);
        return;
    }

    LOG_CONTEXT_CHILDREN_DEBUG("Remove error: child '" + name.toUtf8() + "' not found in context " + getPath().toUtf8());
}

void AbstractContext::reorderChild(Context* child, int index)
{
    if (childrenSortingEnabled)
    {
        throw IllegalStateException("Cannot reorder children when children sorting is enabled");
    }

    boost::lock_guard<boost::mutex> lock(childrenLock);
    std::vector<Context*>::iterator it = std::find(children.begin(), children.end(), child);
    if (it != children.end())
    {
        int oi = it - children.begin();
        children.erase(it);
        int whereInd = index - (oi < index ? 1 : 0);
        children.insert(children.begin() + whereInd, child);
    }
}

void AbstractContext::destroyChild(Context* child, bool moving)
{
    child->destroy(moving);
}

void AbstractContext::destroyChild(const AgString & name, bool moving)
{
    Context* con = getChildWithoutCheckingPerms(name);
    if (con != NULL)
    {
        destroyChild(con, moving);
        return;
    }
    LOG_CONTEXT_CHILDREN_DEBUG("Destroy error: child '" + name.toUtf8() + "' not found in context " + getPath().toUtf8());
}

void AbstractContext::removeAllChildren()
{
    boost::lock_guard<boost::mutex> lock(childrenLock);
    for (std::vector<Context*>::iterator child = children.begin(); child != children.end(); ++child)
    {
        removeChild(*child);
    }
}

void AbstractContext::movePrepare(const AgString & oldPath, const AgString & oldName, const AgString & newPath, const AgString & newName)
{
    UNUSED(oldPath);
    UNUSED(oldName);
    UNUSED(newPath);
    UNUSED(newName);
}

void AbstractContext::moveInternal(const AgString & oldPath, const AgString & oldName, const AgString & newPath, const AgString & newName)
{
    UNUSED(oldName);

    setName(newName);

    boost::lock_guard<boost::mutex> lock(childrenLock);
    for (std::vector<Context*>::iterator child = children.begin(); child != children.end(); ++child)
    {
        AbstractContext * ab_child = static_cast<AbstractContext *>(*child);

        std::vector<AgString> parts1;
        parts1.push_back(oldPath);
        parts1.push_back((*child)->getName());

        std::vector<AgString> parts2;
        parts2.push_back(newPath);
        parts2.push_back((*child)->getName());

        ab_child->moveInternal(ContextUtils::createName(parts1), (*child)->getName(), ContextUtils::createName(parts2), (*child)->getName());
    }
}

void AbstractContext::moveFinalize(const AgString & oldPath, const AgString & oldName, const AgString & newPath, const AgString & newName)
{
    UNUSED(oldPath);
    UNUSED(oldName);
    UNUSED(newPath);
    UNUSED(newName);
}

void AbstractContext::move(Context* newParent, const AgString & newName)
{
    move(getPath(), newParent, newName);
}

void AbstractContext::move(const AgString & oldPath, Context* newParent, const AgString & newName)
{
    LOG_CONTEXT_CHILDREN_DEBUG("Moving context " + getPath().toUtf8() + " to " + newParent->getPath().toUtf8() + " and/or renaming to " + newName.toUtf8());

    AgString oldName = getName();

    std::vector<AgString> parts;
    parts.push_back(newParent->getPath());
    parts.push_back(newName);
    AgString newPath = ContextUtils::createName(parts);

    movePrepare(oldPath, oldName, newPath, newName);

    getParent()->destroyChild(this, true);

    moveInternal(oldPath, oldName, newPath, newName);

    newParent->addChild(this);

    moveFinalize(oldPath, oldName, newPath, newName);
}

Context* AbstractContext::getChild(const AgString & name, CallerControllerPtr caller)
{
    if (!checkPermissions(getChildrenViewPermissions(), caller, this))
    {
        return NULL;
    }

    Context* child = getChildWithoutCheckingPerms(name);

    if (child != NULL && shouldSeeChild(caller, child))
    {
        return child;
    }

    return NULL;
}

Context* AbstractContext::getChild(const AgString & name)
{
    return getChild(name, CallerControllerPtr());
}

Context* AbstractContext::getChildWithoutCheckingPerms(const AgString & name)
{
    std::map<AgString, Context *>::iterator it = childrenMap.find(name);
    if (it != childrenMap.end())
    {
        return it->second;
    }
    return NULL;
}

AgString AbstractContext::getPath()
{
    if (getParent() == NULL)
    {
        return createPath();
    }

    if (path.empty())
    {
        path = createPath();
    }

    return path;
}

AgString AbstractContext::createPath()
{
    Context* con = this;
    AgString nm = getName();

    do
    {
        con = con->getParent();
        if (con != NULL)
        {
            if (con->getParent() != NULL)
            {
                nm = con->getName() + ContextUtils::CONTEXT_NAME_SEPARATOR() + nm;
            }
        }
    }
    while (con != NULL);

    return nm;
}

bool AbstractContext::addEventListener(const AgString & name, ContextEventListenerPtr listener)
{
    return addEventListener(name, listener, false);
}

bool AbstractContext::addEventListener(const AgString & name, ContextEventListenerPtr listener, bool weak)
{
    EventDataPtr ed = getEventData(name);

    if (ed.get() == NULL)
    {
        throw IllegalArgumentException(Cres::get()->getString("conEvtNotAvail") + name);
    }

    {
        boost::lock_guard<boost::mutex> lock(*ed);
        try
        {
            checkPermissions(ed->getDefinition()->getPermissions().get() != NULL ? ed->getDefinition()->getPermissions() : getPermissions(), listener->getCallerController());
        }
        catch (ContextSecurityException ex)
        {
            LOG_CONTEXT_CHILDREN_WARN("Error adding listener '" + listener->toString().toUtf8() + "' of event '" + ed->getDefinition()->getName().toUtf8() + "' in context '" + getPath().toUtf8() + "': " + ex.getMessage().toUtf8());
            return false;
        }
        if (Log::CONTEXT_EVENTS.isDebugEnabled())
        {
            LOG_CONTEXT_EVENTS_DEBUG("Adding '" + listener->toString().toUtf8() + "' as listener of event '" + ed->getDefinition()->getName().toUtf8() + "' in '" + getPath().toUtf8() + "'");
        }

        return ed->addListener(listener, weak);
    }
}

bool AbstractContext::removeEventListener(const AgString & name, ContextEventListenerPtr listener)
{
    EventDataPtr ed = getEventData(name);

    if (ed.get() == NULL)
    {
        LOG_CONTEXT_EVENTS_WARN("Error removing listener of event '" + name.toUtf8() + "' in context '" + getPath().toUtf8() + "': event definition not found");
        return false;
    }

    if (Log::CONTEXT_CHILDREN.isDebugEnabled())
    {
        LOG_CONTEXT_EVENTS_DEBUG("Removing '" + listener->toString().toUtf8() + "' listener of event '" + ed->getDefinition()->getName().toUtf8() + "' in '" + getPath().toUtf8() + "'");
    }

    //synchronized (ed)
    {
        boost::lock_guard<boost::mutex> lock(*ed);
        return ed->removeListener(listener);
    }
}

std::list<VariableDefinitionPtr>  AbstractContext::getVariableDefinitions(CallerControllerPtr caller)
{
    return getVariableDefinitions(caller, false);
}

std::list<VariableDefinitionPtr>  AbstractContext::getVariableDefinitions(CallerControllerPtr caller, bool includeHidden)
{
    std::list<VariableDefinitionPtr> list;

    bool debug = false;
    if (caller.get() != NULL)
    {
        std::map<AgString, AgString> props = caller->getProperties();
        debug = props.find(CALLER_CONTROLLER_PROPERTY_DEBUG) != props.end();
    }

    boost::lock_guard<boost::mutex> lock(variableDataLock);

    for (std::map<AgString, VariableDataPtr>::iterator d = variableData.begin(); d != variableData.end(); ++d)
    {
        VariableDefinitionPtr def = d->second->getDefinition();
        if ((caller.get() == NULL || caller->isPermissionCheckingEnabled()) && !includeHidden && def->isHidden() && !debug)
        {
            continue;
        }

        bool readAccessGranted = checkPermissions(def->getReadPermissions().get() != NULL ? def->getReadPermissions() : getPermissions(), caller, this);
        bool writeAccessGranted = checkPermissions(def->getWritePermissions().get() != NULL ? def->getWritePermissions() : getPermissions(), caller, this);

        if (!readAccessGranted && !writeAccessGranted)
        {
            continue;
        }

        if ((def->isReadable() == readAccessGranted) && (def->isWritable() == writeAccessGranted))
        {
            list.push_back(def);
        }
        else
        {
            VariableDefinition *clone = def->clone();

            clone->setReadable(def->isReadable() ? readAccessGranted : false);
            clone->setWritable(def->isWritable() ? writeAccessGranted : false);

            list.push_back(VariableDefinitionPtr(clone));
        }
    }

    return list;
}

std::list<VariableDefinitionPtr>  AbstractContext::getVariableDefinitions()
{
    return getVariableDefinitions(CallerControllerPtr());
}

std::list<VariableDefinitionPtr>  AbstractContext::getVariableDefinitions(CallerControllerPtr caller, const AgString & group)
{
    std::list<VariableDefinitionPtr> defs;

    std::list<VariableDefinitionPtr> vardefs = getVariableDefinitions(caller);
    for (std::list<VariableDefinitionPtr>::iterator vd = vardefs.begin(); vd != vardefs.end(); ++vd)
    {
        if ((!(*vd)->getGroup().empty() && (group == (*vd)->getGroup())) || (*vd)->getGroup().startsWith(group + ContextUtils::ENTITY_GROUP_SEPARATOR()))
        {
            defs.push_back(*vd);
        }
    }

    return defs;
}

std::list<VariableDefinitionPtr>  AbstractContext::getVariableDefinitions(const AgString & group)
{
    return getVariableDefinitions(CallerControllerPtr(), group);
}

PermissionCheckerPtr AbstractContext::getPermissionChecker()
{
    return permissionChecker;
}

PermissionsPtr AbstractContext::getChildrenViewPermissions()
{
    return childrenViewPermissions.get() != NULL ? childrenViewPermissions : getPermissions();
}

ContextManager* AbstractContext::getContextManager()
{
    return contextManager;
}

bool AbstractContext::isSetupComplete()
{
    return setupComplete;
}

bool AbstractContext::isStarted()
{
    return started;
}

bool AbstractContext ::isInitializedInfo()
{
    return setupComplete;
}

bool AbstractContext ::isInitializedChildren()
{
    return setupComplete;
}

bool AbstractContext::isInitializedVariables()
{
    return setupComplete;
}

bool AbstractContext::isInitializedFunctions()
{
    return setupComplete;
}

bool AbstractContext::isInitializedEvents()
{
    return setupComplete;
}

std::list<FunctionDefinitionPtr>  AbstractContext::getFunctionDefinitions(CallerControllerPtr caller)
{
    return getFunctionDefinitions(caller, false);
}

std::list<FunctionDefinitionPtr>  AbstractContext::getFunctionDefinitions(CallerControllerPtr caller, bool includeHidden)
{
    std::list<FunctionDefinitionPtr> list;

    bool debug;
    if (caller.get() != NULL)
    {
        std::map<AgString, AgString> props = caller->getProperties();
        debug = props.find(CALLER_CONTROLLER_PROPERTY_DEBUG) != props.end();
    }
    else
    {
        debug = false;
    }

    {
        boost::lock_guard<boost::mutex> lock(functionDataLock);

        for (std::map<AgString, FunctionDataPtr>::iterator d = functionData->begin(); d != functionData->end(); ++d)
        {
            FunctionDataPtr ppp = d->second;
            FunctionDefinitionPtr def = d->second->getDefinition();

            if (!checkPermissions(def->getPermissions() != NULL ? def->getPermissions() : getPermissions(), caller, this))
            {
                continue;
            }

            if ((caller.get() == NULL || caller->isPermissionCheckingEnabled()) && !includeHidden && def->isHidden() && !debug)
            {
                continue;
            }

            list.push_back(def);
        }
    }

    return list;
}

std::list<FunctionDefinitionPtr>  AbstractContext::getFunctionDefinitions()
{
    return getFunctionDefinitions(CallerControllerPtr());
}

std::list<FunctionDefinitionPtr>  AbstractContext ::getFunctionDefinitions(CallerControllerPtr caller, const AgString & group)
{
    std::list<FunctionDefinitionPtr> defs;

    std::list<FunctionDefinitionPtr> funcs = getFunctionDefinitions(caller);
    for (std::list<FunctionDefinitionPtr>::iterator fd = funcs.begin(); fd != funcs.end(); ++fd )
    {
        if ((!(*fd)->getGroup().empty() && (group == (*fd)->getGroup())) || (*fd)->getGroup().startsWith(group + ContextUtils::ENTITY_GROUP_SEPARATOR()))
        {
            defs.push_back(*fd);
        }
    }

    return defs;
}

std::list<FunctionDefinitionPtr>  AbstractContext::getFunctionDefinitions(const AgString & group)
{
    return getFunctionDefinitions(CallerControllerPtr(), group);
}

AgString AbstractContext::getType()
{
    return !type.empty() ? type : ContextUtils::getTypeForClass(typeid(this).name());
}

bool AbstractContext::isPermissionCheckingEnabled()
{
    return permissionCheckingEnabled;
}

AgString AbstractContext::getIconId()
{
    return iconId;
}

boost::optional<int> AbstractContext::getIndex()
{
    return index;
}

AgString AbstractContext::getGroup()
{
    return group;
}

AgString AbstractContext::getLocalRoot()
{
    return Contexts::CTX_ROOT;
}

bool AbstractContext::isProxy()
{
    return false;
}

bool AbstractContext::isDistributed()
{
    return false;
}

AgString AbstractContext::getRemoteRoot()
{
    return AgString();
}

AgString AbstractContext::getRemotePath()
{
    return getPath();
}

AgString AbstractContext::getRemotePrimaryRoot()
{
    return AgString();
}

void AbstractContext::setType(const AgString & type)
{
    if (!ContextUtils::isValidContextType(type))
    {
        throw IllegalArgumentException(Cres::get()->getString("conIllegalType") + type);
    }

    AgString old = this->type;
    this->type = type;

    if (old.empty() || (old !=type))
    {
        contextInfoChanded();
    }
}

void AbstractContext::setPermissionCheckingEnabled(bool permissionCheckingEnabled)
{
    this->permissionCheckingEnabled = permissionCheckingEnabled;
}

void AbstractContext::setIconId(const AgString & iconId)
{
    AgString old = this->iconId;
    this->iconId = iconId;

    if (old.empty() || old != iconId )
    {
        contextInfoChanded();
    }
}

void AbstractContext::contextInfoChanded()
{
    if (setupComplete)
    {
        ContextManager* cm = getContextManager();
        if (cm != NULL)
        {
            cm->contextInfoChanged(this);
        }

        if (fireUpdateEvents)
        {
            EventDefinitionPtr ed = getEventDefinition(E_INFO_CHANGED);
            if (ed.get() != NULL)
            {
                fireEvent(E_INFO_CHANGED, createContextInfoTable());
            }
        }
    }
}

void AbstractContext::setIndex(int  index)
{
    this->index = index;
}

void AbstractContext::setGroup(const AgString & group)
{
    AgString old = this->group;
    this->group = group;

    if (old.empty() || old != group)
    {
        contextInfoChanded();
    }
}

std::list<EventDefinitionPtr>  AbstractContext::getEventDefinitions(CallerControllerPtr caller)
{
    return getEventDefinitions(caller, false);
}

std::list<EventDefinitionPtr>  AbstractContext::getEventDefinitions(CallerControllerPtr caller, bool includeHidden)
{
    std::list<EventDefinitionPtr> list;

    bool debug = false;
    if (caller.get() != NULL)
    {
        std::map<AgString, AgString> props = caller->getProperties();
        debug = props.find(CALLER_CONTROLLER_PROPERTY_DEBUG) != props.end();
    }

    boost::lock_guard<boost::mutex> lock(eventDataLock);
    for (std::map<AgString, EventDataPtr>::iterator d = eventData.begin(); d != eventData.end(); ++d)
    {
        if (!checkPermissions(d->second->getDefinition()->getPermissions() != NULL ? d->second->getDefinition()->getPermissions() : getPermissions(), caller, this))
        {
            continue;
        }

        if ((caller.get() == NULL || caller->isPermissionCheckingEnabled()) && !includeHidden && d->second->getDefinition()->isHidden() && !debug)
        {
            continue;
        }

        list.push_back(d->second->getDefinition());
    }

    return list;
}

std::list<EventDefinitionPtr>  AbstractContext::getEventDefinitions()
{
    return getEventDefinitions(CallerControllerPtr());
}

std::list<EventDefinitionPtr>  AbstractContext::getEventDefinitions(CallerControllerPtr caller, const AgString & group)
{
    std::list<EventDefinitionPtr> res;

    std::list<EventDefinitionPtr> events = getEventDefinitions(caller);
    for (std::list<EventDefinitionPtr>::iterator ed = events.begin(); ed != events.end(); ++ed)
    {
        if ((!(*ed)->getGroup().empty() && (group == (*ed)->getGroup())) || (*ed)->getGroup().startsWith(group + ContextUtils::ENTITY_GROUP_SEPARATOR()))
        {
            res.push_back(*ed);
        }
    }

    return res;
}

std::list<EventDefinitionPtr>  AbstractContext::getEventDefinitions(const AgString & group)
{
    return getEventDefinitions(CallerControllerPtr(), group);
}

ActionDefinitionPtr AbstractContext::getActionDefinition(const AgString & name)
{
    boost::lock_guard<boost::mutex> lock(actionDefinitionsLock);

    for (std::vector<ActionDefinitionPtr>::iterator def = actionDefinitions.begin(); def != actionDefinitions.end(); ++def)
    {
        if ((*def)->getName() == name)
        {
            return *def;
        }
    }

    return ActionDefinitionPtr();
}


ActionDefinitionPtr AbstractContext::getActionDefinition(const AgString & name, CallerControllerPtr caller)
{
    std::list<ActionDefinitionPtr> actions = getActionDefinitions(caller, true);
    for (std::list<ActionDefinitionPtr>::iterator ad = actions.begin(); ad != actions.end(); ++ad)
    {
        if ((*ad)->getName() == name)
        {
            return *ad;
        }
    }

    return ActionDefinitionPtr();
}

ActionDefinitionPtr AbstractContext::getDefaultActionDefinition(CallerControllerPtr caller)
{
    std::list<ActionDefinitionPtr> actions = getActionDefinitions(caller, true);
    for (std::list<ActionDefinitionPtr>::iterator ad = actions.begin(); ad != actions.end(); ++ad)
    {
        if ((*ad)->isDefault())
        {
            return *ad;
        }
    }

    return ActionDefinitionPtr();
}

std::list<ActionDefinitionPtr>  AbstractContext::getActionDefinitions(CallerControllerPtr caller)
{
    return getActionDefinitions(caller, false);
}

void AbstractContext::addActionDefinition(ActionDefinitionPtr def)
{
    struct compareActionDefinition
    {
        inline bool operator() (const ActionDefinitionPtr& struct1, const ActionDefinitionPtr& struct2)
        {
            return struct1->compareTo(struct2.get()) <= 0;
        }
    };

    if (def->getName().empty())
    {
        throw IllegalArgumentException("Action name can't be NULL");
    }

    if (getActionDefinition(def->getName()).get() != NULL)
    {
        throw IllegalArgumentException("Action '" + def->getName() + "' is already defined in context '" + getPath() + "'");
    }

    boost::lock_guard<boost::mutex> lock(actionDefinitionsLock);

    actionDefinitions.push_back(def);
    std::sort(actionDefinitions.begin(), actionDefinitions.end());

    if (isSetupComplete() && isFireUpdateEvents())
    {
        EventDefinitionPtr ed = getEventDefinition(E_ACTION_ADDED);
        if (ed.get() != NULL)
        {
            fireEvent(ed->getName(), actDefToDataRecord(def)->wrap());
        }
    }
}

std::list<ActionDefinitionPtr>  AbstractContext::getActionDefinitions(CallerControllerPtr caller, bool includeHidden)
{
    std::list<ActionDefinitionPtr> list;

    std::map<AgString, AgString> props = caller->getProperties();
    bool debug = caller.get() != NULL ? (props.find(CALLER_CONTROLLER_PROPERTY_DEBUG) != props.end()) : false;

    boost::lock_guard<boost::mutex> lock(actionDefinitionsLock);
    for (std::vector<ActionDefinitionPtr>::iterator d = actionDefinitions.begin(); d != actionDefinitions.end(); ++d)
    {
        if (!checkPermissions((*d)->getPermissions().get() != NULL ? (*d)->getPermissions() : getPermissions(), caller, this))
        {
            continue;
        }

        if ((*d)->isHidden() && !debug && !includeHidden)
        {
            continue;
        }

        list.push_back(*d);
    }

    return list;
}

std::list<ActionDefinitionPtr>  AbstractContext::getActionDefinitions()
{
    return getActionDefinitions(CallerControllerPtr());
}

void AbstractContext::removeActionDefinition(const AgString & name)
{
    ActionDefinitionPtr def = getActionDefinition(name);

    boost::lock_guard<boost::mutex> lock(actionDefinitionsLock);


    std::vector<ActionDefinitionPtr>::iterator it = std::find(actionDefinitions.begin(), actionDefinitions.end(), def);

    if (it != actionDefinitions.end())
    {
        actionDefinitions.erase(it);

        if (isSetupComplete() && isFireUpdateEvents())
        {
            EventDefinitionPtr ed = getEventDefinition(E_ACTION_REMOVED);
            if (ed.get() != NULL)
            {
                fireEvent(ed->getName(), name);
            }
        }
    }
}

ActionDefinitionPtr AbstractContext::actDefFromDataRecord(DataRecordPtr rec)
{
    BasicActionDefinitionPtr def = BasicActionDefinitionPtr(new BasicActionDefinition(rec->getString(ActionConstants::FIELD_AD_NAME)));
    def->setDescription(rec->getString(ActionConstants::FIELD_AD_DESCRIPTION));
    def->setHelp(rec->getString(ActionConstants::FIELD_AD_HELP));

    AgString accelerator = rec->getString(ActionConstants::FIELD_AD_ACCELERATOR);
    if (accelerator.empty())
    {
        def->setAccelerator(KeyStrokePtr(new KeyStroke(accelerator)));
    }

    DataTablePtr dropSourcesTable = rec->getDataTable(ActionConstants::FIELD_AD_DROP_SOURCES);
    if (dropSourcesTable.get() != NULL && dropSourcesTable->getRecordCount() > 0)
    {
        std::list<ResourceMaskPtr> dropSources;

        for (int i = 0; i < dropSourcesTable->getRecordCount(); i++)
        {
            DataRecordPtr ds = dropSourcesTable->getRecord(i);
            dropSources.push_back( ResourceMaskPtr(new TreeMask(ds->getString(ActionConstants::FIELD_AD_RESOURCE_MASKS_RESOURCE_MASK))) );
        }

        def->setDropSources(dropSources);
    }

    def->setHidden(rec->getBoolean(ActionConstants::FIELD_AD_HIDDEN));
    def->setEnabled(rec->getBoolean(ActionConstants::FIELD_AD_ENABLED));
    def->setIconId(rec->getString(ActionConstants::FIELD_AD_ICON_ID));
    def->setGroup(rec->getString(ActionConstants::FIELD_AD_GROUP));

    AgString executionGroup = rec->getString(ActionConstants::FIELD_AD_EXECUTION_GROUP);
    if (!executionGroup.empty())
    {
        def->setExecutionGroup(GroupIdentifierPtr(new GroupIdentifier(executionGroup)));
    }

    def->setDefault(rec->getBoolean(ActionConstants::FIELD_AD_DEFAULT));

    return def;
}
DataTablePtr AbstractContext::getVariable(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request)
{
    //boost::posix_time::ptime startTime = boost::posix_time::microsec_clock::local_time();
    setupVariables();

    VariableDataPtr data = getVariableData(def->getName());

    boost::shared_mutex &mutex = data->getReadWriteLock();

    //todo need check
    //lock(request, data.getReadWriteLock().readLock());
    {
        boost::lock_guard<boost::shared_mutex> lock(mutex);

        try
        {
            checkPermissions(def->getReadPermissions().get() != NULL ? def->getReadPermissions() : getPermissions(), caller);

            //if (Log.CONTEXT_VARIABLES.isDebugEnabled())
            //{
            //    AGDEBUG(DBG_DEBUG, "CONTEXT_VARIABLES", "Trying to get variable '" + def->getName() + "' from context '" + this->getPath() + "'");
            //}

            DataTablePtr result = executeGetter(data, caller, request);

            if (result->isInvalid())
            {
                throw ContextException(result->getInvalidationMessage());
            }

            result = checkVariableValue(def, result);

            //boost::posix_time::ptime endTime = boost::posix_time::microsec_clock::local_time();
            //boost::posix_time::time_duration dur = endTime - startTime;

            //if (dur.total_milliseconds() > LOW_PERFORMANCE_THRESHOLD)
            //{
            //    int level = dur.total_milliseconds() > VERY_LOW_PERFORMANCE_THRESHOLD ? DBG_INFO : DBG_DEBUG;
            //    AGDEBUG(level, "PERFORMANCE", "Getting value of variable '" + def->toString() + "' in context '" + getPath() + "' took " + AgString::fromInt64(dur.total_milliseconds()) + " milliseconds");
            //}

            return result;
        }
        catch (AggreGateException ex)
        {
            throw ContextException(MessageFormat::format(Cres::get()->getString("conErrGettingVar"), def->toString(), toString()) + ex.getMessage(), ex.getDetails());
        }
    }
    data->registerGetOperation();
    return DataTablePtr();
}

DataTablePtr AbstractContext::checkVariableValue(VariableDefinitionPtr def, DataTablePtr val)
{
    if (!valueCheckingEnabled)
    {
        return val;
    }

    DataTablePtr value = val;

    AgString msg = checkVariableValueFormat(def, value);

    if (!msg.empty())
    {
        LOG_CONTEXT_VARIABLES_DEBUG("Invalid value of variable '" + def->getName().toUtf8() + "': " + msg.toUtf8());
        DataTablePtr newValue = getDefaultValue(def);
        DataTableReplication::copy(*value, newValue, true, true, true, true, true);
        std::list<CompatibilityConverterPtr> converters = def->getCompatibilityConverters();
        if (converters.size() != 0)
        {
            for (std::list<CompatibilityConverterPtr>::iterator converter = converters.begin(); converter != converters.end(); ++converter)
            {
                try
                {
                    newValue = (*converter)->convert(value, newValue);
                }
                catch (AggreGateException ex)
                {
                    LOG_CONTEXT_VARIABLES_WARN("Error converting value of variable '" + def->getName().toUtf8() + "': " + ex.getMessage().toUtf8());
                }
            }
        }
        value = newValue;
        checkVariableValueFormat(def, value);
    }
    return value;
}


AgString AbstractContext::checkVariableValueFormat(VariableDefinitionPtr def, DataTablePtr table)
{
    if (!valueCheckingEnabled)
    {
        return "";
    }

    TableFormatPtr requiredFormat = def->getFormat();

    if (requiredFormat.get() != NULL)
    {
        if (table->getRecordCount() > 0)
        {
            AgString msg = table->conformMessage(requiredFormat);
            if (!msg.empty())
                return "Invalid format: " + msg;
        }
        else
        {
            if (def->getFormat()->getMinRecords() != 0)
                return "DataTable with empty records can not be set.";
        }
    }

    return "";
}


DataTablePtr AbstractContext::executeGetter(VariableDataPtr data, CallerControllerPtr caller, RequestControllerPtr request)
{
    DataTablePtr result = executeGetterMethod(data, caller, request);
    if (result.get() != NULL)
    {
        return result;
    }

    VariableDefinitionPtr def = data->getDefinition();

    if (def->getGetter().get() != NULL)
    {
        result = def->getGetter()->get(this, def, caller, request);
    }
    if (result.get() != NULL)
    {
        return result;
    }

    result = getVariableImpl(def, caller, request);
    if (result.get() != NULL)
    {
        return result;
    }

    return executeDefaultGetter(def, caller, false, true); // Setting check to false as we'll check value later
}


DataTablePtr AbstractContext::executeGetterMethod(VariableDataPtr data, CallerControllerPtr caller, RequestControllerPtr request)
{
    if (!data->isGetterCached())
    {
        std::string methodName = (GETTER_METHOD_PREFIX + data->getDefinition()->getName()).toUtf8();
        // here can be:
        // getVvariables
        // getVfunctions
        // getVevents
        // getVactions
        // getVinfo

        if ( (methodName != "getVvariables") && (methodName != "getVfunctions") &&
             (methodName != "getVevents") && (methodName != "getVactions")
             && (methodName != "getVinfo"))
        {
            //not supported
            data->setGetterCached(true);
            return DataTablePtr();
        }
        data->setGetterMethod(methodName);
        data->setGetterCached(true);
    }

    std::string getter = data->getGetterMethod();

    if (getter == "getVvariables")
    {
        return this->getVvariables(data->getDefinition(), caller, request);
    }
    else if (getter == "getVfunctions")
    {
        return this->getVfunctions(data->getDefinition(), caller, request);
    }
    else if (getter == "getVevents")
    {
        return this->getVevents(data->getDefinition(), caller, request);
    }
    else if (getter == "getVactions")
    {
        return this->getVactions(data->getDefinition(), caller, request);
    }
    else if (getter == "getVinfo")
    {
        return this->getVinfo(data->getDefinition(), caller, request);
    }

    return DataTablePtr();
}

DataTablePtr AbstractContext::executeDefaultGetter(const AgString & name, CallerControllerPtr caller)
{
    return executeDefaultGetter(name, caller, true);
}

DataTablePtr AbstractContext::executeDefaultGetter(const AgString & name, CallerControllerPtr caller, bool check)
{
    return executeDefaultGetter(name, caller, check, true);
}

DataTablePtr AbstractContext::executeDefaultGetter(const AgString & name, CallerControllerPtr caller, bool check, bool createDefault)
{
    VariableDefinitionPtr def = getVariableDefinition(name);

    if (def.get() == NULL)
    {
        throw ContextException(MessageFormat::format(Cres::get()->getString("conVarNotAvailExt"), name, getPath()));
    }

    return executeDefaultGetter(def, caller, check, createDefault);
}


DataTablePtr AbstractContext::executeDefaultGetter(VariableDefinitionPtr def, CallerControllerPtr caller, bool check, bool createDefault)
{
    DataTablePtr value = executeDefaultGetterImpl(def, caller);

    if (value.get() == NULL)
    {
        return createDefault ? getDefaultValue(def) : DataTablePtr();
    }

    return check ? checkVariableValue(def, value) : value;
}


DataTablePtr AbstractContext::executeDefaultGetterImpl(VariableDefinitionPtr vd, CallerControllerPtr caller)
{
    UNUSED(caller);

    AgObjectPtr value = getVariableData(vd->getName())->getValue();

    return value.get() != NULL ? boost::static_pointer_cast<DataTable>(value) : getDefaultValue(vd);
}

int AbstractContext::hashCode()
{
    if (getParent() == NULL)
    {
        return Context::hashCode();
    }

    const int prime = 31;
    int result = 1;
    Context* root = getRoot();
    AgString path = getPath();
    result = prime * result + ((root == NULL) ? 0 : root->hashCode());
    result = prime * result + ((path.empty()) ? 0 : path.hashCode());
    return result;
}

bool AbstractContext::equals(AbstractContext *obj)
{
    if (this == obj)
    {
        return true;
    }
    if (obj == NULL)
    {
        return false;
    }
    AbstractContext *other = obj;
    if (getRoot() != other->getRoot())
    {
        return false;
    }
    if (getPath() != other->getPath())
    {
        return false;
    }
    return true;
}

DataTablePtr AbstractContext::getVariable(const AgString & name, CallerControllerPtr caller, RequestControllerPtr request)
{
    return getVariable(getAndCheckVariableDefinition(name), caller, request);
}

DataTablePtr AbstractContext::getVariable(const AgString & name, CallerControllerPtr caller)
{
    return getVariable(getAndCheckVariableDefinition(name), caller, RequestControllerPtr());
}

DataTablePtr AbstractContext::getVariable(const AgString & name)
{
    return getVariable(getAndCheckVariableDefinition(name), CallerControllerPtr(), RequestControllerPtr());
}

DataTablePtr AbstractContext::getVariableImpl(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request)
{
    UNUSED(def);
    UNUSED(caller);
    UNUSED(request);
    return DataTablePtr();
}

AgObjectPtr AbstractContext::getVariableObject(const AgString & /*name*/, CallerControllerPtr caller)
{
    assert(0);
    UNUSED(caller);
    /*try
    {
        VariableDefinitionPtr def = getAndCheckVariableDefinition(name);
        VariableDataPtr data = getVariableData(name);


        boost::shared_mutex &mutex =  data->getReadWriteLock();

        boost::lock_guard<boost::shared_mutex> lock(mutex);


        if (isSetupComplete() && data->getValue().get() != NULL)
        {
            return data->getValue();
        }

        if (def->getValueClass().empty())
        {
            throw ContextException("Value class not defined for variable: " + def->toDetailedString());
        }

        AgObjectPtr value;

        DataTablePtr table = getVariable(name, caller);

        std::vector<AgObjectPtr> list = DataTableConversion::instance().beansFromTable(table, def->getValueClass(), def->getFormat(), true);

        if (def->getFormat()->isSingleRecord())
        {
            value = list[0];
        }
        else
        {
            value = list;
        }

            // Caching must be disabled if write lock is held by current thread (e.g. this method is called from variable setter)
            if (isSetupComplete() && def.isLocalCachingEnabled() && !data.getReadWriteLock().isWriteLockedByCurrentThread())
            {
              data.setValue(value);
            }

            return value;*/
    /*}
    catch (AggreGateException ex)
    {
        throw ContextRuntimeException(ex.getMessage(), ex.getDetails());
    }*/
    return AgObjectPtr();
}

void AbstractContext::setVariable(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr value)
{
    boost::posix_time::ptime startTime = boost::posix_time::microsec_clock::local_time();

    setupVariables();

    VariableDataPtr data = getVariableData(def->getName());


    boost::shared_mutex &mutex = data->getReadWriteLock();
    {
        boost::lock_guard<boost::shared_mutex> lock(mutex);


        if (value.get() == NULL)
        {
            throw ContextException("Value cannot be NULL");
        }

        DataTablePtr resultingValue = value;
        try
        {
            checkPermissions(def->getWritePermissions().get() != NULL ? def->getWritePermissions() : getPermissions(), caller);

            if (!def->isWritable() && caller.get() != NULL && caller->isPermissionCheckingEnabled())
            {
                throw ContextException(Cres::get()->getString("conVarReadOnly"));
            }

            if (Log::CONTEXT_VARIABLES.isDebugEnabled())
            {
                LOG_CONTEXT_VARIABLES_DEBUG("Trying to set variable '" + def->getName().toUtf8() + "' in context '" + this->getPath().toUtf8() + "'");
            }

            if (value->isInvalid())
            {
                throw ContextException(value->getInvalidationMessage());
            }

            if (value->getTimestamp() == NULL)
            {
                value->setTimestamp(AgDatePtr(new AgDate()));
            }

            // Preventing changes to read-only fields to be made by "client" callers (i.e. ones with permission checking enabled)
            if (def->getFormat().get() != NULL && def->getFormat()->hasReadOnlyFields() && caller.get() != NULL && caller->isPermissionCheckingEnabled())
            {
                resultingValue = getVariable(def, caller, request);
                DataTableReplication::copy(*value, resultingValue, false, true, true, true, true);
                checkVariableValueFormat(def, resultingValue);
            }

            AgString msg = checkVariableValueFormat(def, resultingValue);

            if (!msg.empty())
            {
                LOG_CONTEXT_VARIABLES_DEBUG("Invalid value of variable '" + def->getName().toUtf8() + "': " + msg.toUtf8() + " (value: " + resultingValue->toString().toUtf8() + ")");
                value = resultingValue;
                resultingValue = getVariable(def, caller, request);
                DataTableReplication::copy(*value, resultingValue, true, true, true, true, true);
                checkVariableValueFormat(def, resultingValue);
            }

            if (def->isLocalCachingEnabled())
            {
                data->setValue(AgObjectPtr()); // Resetting cache
            }

            executeSetter(data, caller, request, resultingValue);

            variableUpdated(def, caller, resultingValue);

            boost::posix_time::time_duration dur = boost::posix_time::microsec_clock::local_time() - startTime;

            if (dur.total_milliseconds() > LOW_PERFORMANCE_THRESHOLD)
            {
                int level = dur.total_milliseconds() > VERY_LOW_PERFORMANCE_THRESHOLD ? log4cpp::Priority::INFO : log4cpp::Priority::DEBUG;
                std::string str = "Setting value of variable '" + def->toString().toUtf8() + "' in context '" + getPath().toUtf8() + "' took " + AgString::fromInt64(dur.total_milliseconds()).toUtf8() + " milliseconds";
                if(level == log4cpp::Priority::INFO)
                {
                    LOG_PERFORMANCE_INFO(str);
                }
                else
                {
                    LOG_PERFORMANCE_DEBUG(str);
                }
            }
        }
        catch (ValidationException ex)
        {
            throw ex;
        }
        catch (AggreGateException ex)
        {
            throw ContextException(MessageFormat::format(Cres::get()->getString("conErrSettingVar"), def->toString(), toString()) + ex.getMessage(), ex.getDetails());
        }
    }
    data->registerSetOperation();
}

void AbstractContext::variableUpdated(VariableDefinitionPtr def, CallerControllerPtr caller, DataTablePtr value)
{
    fireUpdatedEvent(def, caller, value);
    fireChangeEvent(def, caller, AgDatePtr(), value);
}

void AbstractContext::fireUpdatedEvent(VariableDefinitionPtr def, CallerControllerPtr caller, DataTablePtr value)
{
    bool callerAllowsUpdatedEvents;
    if (caller)
    {
        std::map<AgString, AgString> props = caller->getProperties();
        callerAllowsUpdatedEvents = props.find(CALLER_CONTROLLER_PROPERTY_NO_UPDATED_EVENTS) == props.end();
    }
    else
    {
        callerAllowsUpdatedEvents = true;
    }

    if (setupComplete && fireUpdateEvents && def->isAllowUpdateEvents() && callerAllowsUpdatedEvents)
    {
        EventDefinitionPtr ed = getEventDefinition(E_UPDATED);
        if (ed.get() != NULL)
        {
            std::list<AgObjectPtr> list;
            list.push_back(AgObjectPtr(new AgString(def->getName())));
            list.push_back(value);
            list.push_back(AgObjectPtr(new AgString(caller.get() != NULL ? caller->getUsername() : "")));
            fireEvent(E_UPDATED, list);
        }
    }
}

void AbstractContext::fireChangeEvent(VariableDefinitionPtr def, CallerControllerPtr caller, AgDatePtr timestamp, DataTablePtr value)
{
    bool callerAllowsChangeEvents;
    if (caller)
    {
        std::map<AgString, AgString> props = caller->getProperties();
        callerAllowsChangeEvents = props.find(CALLER_CONTROLLER_PROPERTY_NO_CHANGE_EVENTS) == props.end();
    }
    else
    {
        callerAllowsChangeEvents = true;
    }

    if (setupComplete && fireUpdateEvents && def->isAllowUpdateEvents() && callerAllowsChangeEvents)
    {
        EventDefinitionPtr ed = getEventDefinition(E_CHANGE);
        if (ed.get() != NULL)
        {
            FireChangeEventRequestControllerPtr fer = FireChangeEventRequestControllerPtr(new FireChangeEventRequestController(def->getChangeEventsExpirationPeriod(), def, value));

            std::list<AgObjectPtr> list;
            list.push_back(AgObjectPtr(new AgString(def->getName())));

            DataTablePtr eventData = DataTablePtr(new DataTable(ed->getFormat(), list));

            fireEvent(ed, eventData, EventLevel::NONE(), boost::optional<int64_t>(), timestamp, 0, caller, fer, PermissionsPtr());
        }
    }
}
void AbstractContext::setupVariables()
{
}

void AbstractContext::executeSetter(VariableDataPtr data, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr value)
{
    VariableDefinitionPtr def = data->getDefinition();

    if (executeSetterMethod(data, caller, request, value))
    {
        return;
    }

    if (def->getSetter().get() != NULL)
    {
        if (def->getSetter()->set(this, def, caller, request, value))
        {
            return;
        }
    }

    if (setVariableImpl(def, caller, request, value))
    {
        return;
    }

    executeDefaultSetter(def, caller, value);
}

bool AbstractContext::executeSetterMethod(VariableDataPtr data, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr value)
{
    if (!data->isSetterCached())
    {
        std::string setter = (SETTER_METHOD_PREFIX + data->getDefinition()->getName()).toUtf8();
        //here only one method setVariable(...)
        if (setter != "setVariable")
        {
            return false;
        }

        data->setSetterMethod(setter);
        data->setSetterCached(true);
    }

    std::string setter = data->getSetterMethod();
    if (setter == "setVariable")
    {
        this->setVariable(data->getDefinition(), caller, request, value);
    }
    else
    {
        assert(0);
    }

    return false;
}


DataTablePtr AbstractContext::getDefaultValue(VariableDefinitionPtr def)
{
    if (def->getDefaultValue().get() != NULL)
    {
        return def->getDefaultValue();
    }

    return DataTablePtr(new DataTable(def->getFormat(), true));
}


void AbstractContext::executeDefaultSetter(const AgString & name, CallerControllerPtr caller, ::DataTablePtr value)
{
    VariableDefinitionPtr def = getVariableDefinition(name);

    if (def.get() == NULL)
    {
        throw ContextException(MessageFormat::format(Cres::get()->getString("conVarNotAvailExt"), name, getPath()));
    }

    executeDefaultSetter(def, caller, value);
}


void AbstractContext::executeDefaultSetter(VariableDefinitionPtr def, CallerControllerPtr caller, DataTablePtr value)
{
    executeDefaultSetterImpl(def, caller, value);
}

void AbstractContext::executeDefaultSetterImpl(VariableDefinitionPtr vd, CallerControllerPtr caller, DataTablePtr value)
{
    UNUSED(caller);
    getVariableData(vd->getName())->setValue(value);
}

void AbstractContext::setVariable(const AgString & name, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr value)
{
    VariableDefinitionPtr def = getAndCheckVariableDefinition(name);
    setVariable(def, caller, request, value);
}

void AbstractContext::setVariable(const AgString & name, CallerControllerPtr caller, DataTablePtr value)
{
    setVariable(name, caller, RequestControllerPtr(), value);
}

void AbstractContext::setVariable(const AgString & name, DataTablePtr value)
{
    setVariable(name, CallerControllerPtr(), RequestControllerPtr(), value);
}

void AbstractContext::setVariable(const AgString & name, CallerControllerPtr caller, std::list<AgObjectPtr> values)
{
    VariableDefinitionPtr def = getAndCheckVariableDefinition(name);
    setVariable(name, caller, RequestControllerPtr(), DataTablePtr(new DataTable(def->getFormat(), values)));
}

void AbstractContext::setVariable(const AgString & name, std::list<AgObjectPtr> value)
{
    setVariable(name, CallerControllerPtr(), value);
}

bool AbstractContext::setVariableImpl(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr value)
{
    UNUSED(def);
    UNUSED(caller);
    UNUSED(request);
    UNUSED(value);
    return false;
}

VariableDefinitionPtr AbstractContext::getAndCheckVariableDefinition(const AgString & name)
{
    setupVariables();
    VariableDefinitionPtr def = getVariableDefinition(name);
    if (def.get() == NULL)
    {

        throw ContextException(MessageFormat::format(Cres::get()->getString("conVarNotAvailExt"), name, getPath()));
    }
    return def;
}

bool AbstractContext::setVariableField(const AgString & variable, const AgString & field,
                                       AgObjectPtr value, CallerControllerPtr cc)
{
    return setVariableField(variable, field, 0, value, cc);
}

bool AbstractContext::setVariableField(const AgString & variable, const AgString & field, int record, AgObjectPtr value, CallerControllerPtr cc)
{
    DataTablePtr tab = getVariable(variable, cc);
    AgObjectPtr old = tab->getRecord(record)->getValue(field);
    tab->getRecord(record)->setValue(field, value);
    setVariable(variable, cc, tab);
    return old.get() == NULL ? value.get() != NULL : !old->equals(&(*value));
}

void AbstractContext::setVariableField(const AgString & variable, const AgString & field, AgObjectPtr value, const AgString & compareField, AgObjectPtr compareValue, CallerControllerPtr cc)
{
    DataTablePtr tab = getVariable(variable, cc);
    DataRecordPtr rec = tab->select(compareField, compareValue);
    if (rec.get() != NULL)
    {
        rec->setValue(field, value);
    }
    else
    {
        throw ContextException("Record with " + compareField + "=" + compareValue->toString() + " not found");
    }
    setVariable(variable, cc, tab);
}

void AbstractContext::addVariableRecord(const AgString & variable, CallerControllerPtr cc, DataRecordPtr record)
{
    DataTablePtr tab = getVariable(variable, cc);
    tab->addRecord(record);
    setVariable(variable, cc, tab);
}

void AbstractContext::addVariableRecord(const AgString & variable, CallerControllerPtr cc, std::list<AgObjectPtr> recordData)
{
    DataTablePtr tab = getVariable(variable, cc);
    DataRecordPtr rec = tab->addRecord();
    for (std::list<AgObjectPtr>::iterator it = recordData.begin(); it != recordData.end(); ++it)
    {
        rec->addValue(*it);
    }
    setVariable(variable, cc, tab);
}

void AbstractContext::removeVariableRecords(const AgString & variable, CallerControllerPtr cc, const AgString & field, AgObjectPtr value)
{
    DataTablePtr tab = getVariable(variable, cc);

    std::vector<DataRecordPtr>::iterator it = tab->iteratorBegin();
    while (it != tab->iteratorEnd())
    {
        DataRecordPtr rec = *it;
        AgObjectPtr agob = rec->getValue(field);
        if (Util::equals(agob, value))
        {
            tab->removeIterator(it++);
        }
        else
        {
            ++it;
        }
    }

    setVariable(variable, cc, tab);
}


DataTablePtr AbstractContext::callFunction(FunctionDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr parameters)
{
    boost::posix_time::ptime startTime = boost::posix_time::microsec_clock::local_time();

    setupFunctions();

    FunctionDataPtr data = getFunctionData(def->getName());

    boost::recursive_mutex &mutex = data->getExecutionLock();

    if (!def->isConcurrent())
    {
        //mutex.lock(); // TODO fix deadlock
    }

    try
    {
        checkPermissions(def->getPermissions().get() != NULL ? def->getPermissions() : getPermissions(), caller);

        LOG_CONTEXT_FUNCTIONS_DEBUG("Trying to call function '" + def->getName().toUtf8() + "' of context '" + getPath().toUtf8() + "'");

        if (def->getPermissions().get() != NULL)
        {
            checkPermissions(def->getPermissions(), caller);
        }

        TableFormatPtr requiredInputFormat = def->getInputFormat();
        TableFormatPtr requiredOutputFormat = def->getOutputFormat();

        if (parameters->isInvalid())
        {
            throw ContextException(parameters->getInvalidationMessage());
        }

        if (valueCheckingEnabled && requiredInputFormat.get() != NULL)
        {
            AgString msg = parameters->conformMessage(requiredInputFormat);
            if (!msg.empty())
            {
                LOG_CONTEXT_FUNCTIONS_DEBUG("Invalid input format of function '" + def->getName().toUtf8() + "': " + msg.toUtf8());

                DataTablePtr newParameters = DataTablePtr(new DataTable(def->getInputFormat(), true));
                DataTableReplication::copy(*parameters, newParameters, true, true, true, true, true);
                parameters = newParameters;

                msg = parameters->conformMessage(requiredInputFormat);
                if (!msg.empty())
                {
                    throw ContextException("Invalid format: " + msg);
                }
            }
        }

        DataTablePtr result = executeImplementation(data, caller, request, parameters);

        if (result->isInvalid())
        {
            throw ContextException(result->getInvalidationMessage());
        }

        if (result->getRecordCount() == 0 && result->getFormat()->getFieldCount() == 0)
        {
            result->setFormat(def->getOutputFormat());
        }

        if (valueCheckingEnabled && requiredOutputFormat.get() && result.get())
        {
            AgString msg = result.get()->conformMessage(requiredOutputFormat);
            if (!msg.empty())
            {
                throw ContextException("Function '" + def->getName() + "' of context '" + getPath() + "' returned value of invalid format: " + msg);
            }
        }

        boost::posix_time::time_duration dur = boost::posix_time::microsec_clock::local_time() - startTime;
        if (dur.total_milliseconds() > LOW_PERFORMANCE_THRESHOLD)
        {
            int level = dur.total_milliseconds() > VERY_LOW_PERFORMANCE_THRESHOLD ? log4cpp::Priority::INFO : log4cpp::Priority::DEBUG;
            std::string str = "Function '" + def->toString().toUtf8() + "' in context '" + getPath().toUtf8() + "' was executing for " + AgString::fromInt64(dur.total_milliseconds()).toUtf8() + " milliseconds";
            if(level == log4cpp::Priority::INFO)
            {
                LOG_PERFORMANCE_INFO(str);
            }
            else
            {
                LOG_PERFORMANCE_DEBUG(str);
            }
        }

        return result;
    }
    catch (ContextException ex)
    {
        if (!def->isConcurrent())
        {
            mutex.unlock();
        }
        data->registerExecution();
        throw ex;
    }
    catch (AggreGateException ex)
    {
        if (!def->isConcurrent())
        {
            mutex.unlock();
        }
        data->registerExecution();
        throw ContextException(MessageFormat::format(Cres::get()->getString("conErrCallingFunc"), def->toString(), toString()) + ex.getMessage(), ex.getDetails());
    }

    if (!def->isConcurrent())
    {
        mutex.unlock();
    }
    data->registerExecution();

    return DataTablePtr();
}

DataTablePtr AbstractContext::executeImplementation(FunctionDataPtr data, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr parameters)
{
    DataTablePtr result = executeImplementationMethod(data, caller, request, parameters);

    if (result.get() != NULL)
    {
        return result;
    }

    FunctionDefinitionPtr def = data->getDefinition();

    if (def->getImplementation().get() != NULL)
    {
        result = def->getImplementation()->execute(this, def, caller, request, parameters);

        if (result.get() != NULL)
        {
            return result;
        }

        return getDefaultFunctionOutput(def);
    }

    result = callFunctionImpl(def, caller, request, parameters);

    if (result.get() != NULL)
    {
        return result;
    }

    throw ContextException(MessageFormat::format(Cres::get()->getString("conFuncNotImpl"), def->getName(), getPath()));
}


DataTablePtr AbstractContext::executeImplementationMethod(FunctionDataPtr data, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr parameters)
{
    FunctionDefinitionPtr def = data->getDefinition();

    if (!data->isImplementationCached())
    {
        std::string implementation = (IMPLEMENTATION_METHOD_PREFIX + def->getName()).toUtf8();
        data->setImplementationMethod(implementation);
        data->setImplementationCached(true);
        if (implementation != "callFgetCopyData" && implementation != "callFcopy" && implementation != "callFcopyToChildren")
        {
            return DataTablePtr();
        }
    }

    std::string implementation = data->getImplementationMethod();

    DataTablePtr result;
    if (implementation == "callFgetCopyData")
    {
        result = this->callFgetCopyData(def, caller, request, parameters);
    }
    else if (implementation == "callFcopy")
    {
        result = this->callFcopy(def, caller, request, parameters);
    }
    else if (implementation == "callFcopyToChildren")
    {
        result = this->callFcopyToChildren(def, caller, request, parameters);
    }
    else
    {
        return DataTablePtr();
    }

    if (result.get() != NULL)
    {
        return result;
    }

    return getDefaultFunctionOutput(def);
}


DataTablePtr AbstractContext::getDefaultFunctionOutput(FunctionDefinitionPtr def)
{
    TableFormatPtr format = def->getOutputFormat();
    return format.get() != NULL ? DataTablePtr(new DataTable(format, true)) : DataTablePtr(new DataTable());
}


void AbstractContext::setupFunctions()
{
}

DataTablePtr AbstractContext::callFunction(const AgString & name, CallerControllerPtr caller,
                                           RequestControllerPtr request, DataTablePtr parameters)
{
    FunctionDefinitionPtr def = getAndCheckFunctionDefinition(name);
    return callFunction(def, caller, request, parameters);
}

DataTablePtr AbstractContext::callFunction(const AgString & name, CallerControllerPtr caller, ::DataTablePtr parameters)
{
    return callFunction(name, caller, RequestControllerPtr(), parameters);
}

DataTablePtr AbstractContext::callFunction(const AgString & name, DataTablePtr parameters)
{
    return callFunction(getAndCheckFunctionDefinition(name), CallerControllerPtr(),
                        RequestControllerPtr(), parameters);
}

DataTablePtr AbstractContext::callFunction(const AgString & name)
{
    FunctionDefinitionPtr def = getAndCheckFunctionDefinition(name);
    return callFunction(def, CallerControllerPtr(), RequestControllerPtr(),
                        DataTablePtr(new DataTable(def->getInputFormat(), true)));
}

DataTablePtr AbstractContext::callFunction(const AgString & name, CallerControllerPtr caller)
{
    FunctionDefinitionPtr def = getAndCheckFunctionDefinition(name);
    return callFunction(def, caller, RequestControllerPtr(), DataTablePtr(new DataTable(def->getInputFormat(), true)));
}

DataTablePtr AbstractContext::callFunction(const AgString & name, CallerControllerPtr caller, std::list<AgObjectPtr> parameters)
{
    FunctionDefinitionPtr def = getAndCheckFunctionDefinition(name);
    return callFunction(name, caller, DataTablePtr(new DataTable(def->getInputFormat(), parameters)));
}

DataTablePtr AbstractContext::callFunction(const AgString & name, std::list<AgObjectPtr> parameters)
{
    return callFunction(name, CallerControllerPtr(), parameters);
}

DataTablePtr AbstractContext::callFunctionImpl(FunctionDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr parameters)
{
    UNUSED(def);
    UNUSED(caller);
    UNUSED(request);
    UNUSED(parameters);
    return DataTablePtr();
}

FunctionDefinitionPtr AbstractContext::getAndCheckFunctionDefinition(const AgString & name)
{
    setupFunctions();
    FunctionDefinitionPtr def = getFunctionDefinition(name);
    if(def.get() == NULL)
    {
        throw ContextException(MessageFormat::format(Cres::get()->getString("conFuncNotAvailExt"), name, getPath()));
    }
    return def;
}

void AbstractContext::addVariableDefinition(VariableDefinitionPtr def)
{
    if (getVariableDefinition(def->getName()).get() != NULL)
    {
        throw IllegalArgumentException("Variable '" + def->getName() + "' already defined in context '" + getPath() + "'");
    }

    boost::lock_guard<boost::mutex> lock(variableDataLock);

    //    AGDEBUG(DBG_DEBUG, "addVariableDefinition:", def->getName());

    variableData[def->getName()] = VariableDataPtr(new VariableData(def));

    if (setupComplete && fireUpdateEvents && !def->isHidden())
    {
        EventDefinitionPtr ed = getEventDefinition(E_VARIABLE_ADDED);
        if (ed.get() != NULL)
        {
            variableOder[def] = variableCounter;
            variableCounter++;
            fireEvent(ed->getName(), DataTablePtr(new DataTable(varDefToDataRecord(def, CallerControllerPtr()))));
        }
    }

    if (getContextManager() != NULL)
    {
        getContextManager()->variableAdded(this, def);
    }
}

void AbstractContext::removeVariableDefinition(const AgString & name)
{
    removeVariableDefinition(getVariableDefinition(name));
}

void AbstractContext::removeVariableDefinition(VariableDefinitionPtr def)
{
    if (def.get() == NULL)
    {
        return;
    }

    VariableDataPtr data;

    {
        boost::lock_guard<boost::mutex> lock(variableDataLock);
        std::map<AgString, VariableDataPtr>::iterator it = variableData.find(def->getName());
        if (it != variableData.end())
        {
            data = it->second;
            variableData.erase(it);
        }
        else
        {
            assert(0);
        }
    }

    boost::shared_mutex &mutex = data->getReadWriteLock();
    boost::lock_guard<boost::shared_mutex> lockReadWriteLock(mutex);

    {
        boost::lock_guard<boost::mutex> lock(variableStatusesLock);
        std::map<AgString, VariableStatusPtr>::iterator it = variableStatuses.find(def->getName());
        if (it != variableStatuses.end())
        {
            variableStatuses.erase(it);
        }
    }


    if (setupComplete && fireUpdateEvents && !def->isHidden())
    {
        EventDefinitionPtr ed = getEventDefinition(E_VARIABLE_REMOVED);
        if (ed.get() != NULL)
        {
            std::map<VariableDefinitionPtr, int>::iterator it = variableOder.find(def);
            if (it != variableOder.end())
            {
                variableOder.erase(it);
            }

            fireEvent(ed->getName(), def->getName());
        }
    }

    if (getContextManager() != NULL)
    {
        getContextManager()->variableRemoved(this, def);
    }
}

void AbstractContext::addFunctionDefinition(FunctionDefinitionPtr def)
{
    if (getFunctionDefinition(def->getName()).get() != NULL)
    {
        throw IllegalArgumentException("Function '" + def->getName() + "' already defined in context '" + getPath() + "'");
    }

    {
        boost::lock_guard<boost::mutex> lock(functionDataLock);

        (*functionData)[def->getName()] = FunctionDataPtr(new FunctionData(def));
    }

    if (setupComplete && fireUpdateEvents && !def->isHidden())
    {
        EventDefinitionPtr ed = getEventDefinition(E_FUNCTION_ADDED);
        if (ed.get() != NULL)
        {
            functionOder[def] = functionCounter;
            functionCounter++;
            fireEvent(ed->getName(), DataTablePtr(new DataTable(funcDefToDataRecord(def, CallerControllerPtr()))));
        }
    }

    if (getContextManager() != NULL)
    {
        getContextManager()->functionAdded( this, def);
    }
}

void AbstractContext::removeFunctionDefinition(const AgString & name)
{
    removeFunctionDefinition(getFunctionDefinition(name));
}

void AbstractContext::removeFunctionDefinition(FunctionDefinitionPtr def)
{
    if (def.get() == NULL)
    {
        return;
    }

    FunctionDataPtr data;

    {
        boost::lock_guard<boost::mutex> lock(functionDataLock);
        std::map<AgString, FunctionDataPtr>::iterator it = functionData->find(def->getName());
        if (it != functionData->end())
        {
            data = it->second;
            functionData->erase(it);
        }
        else
        {
            assert(0);
        }
    }

    boost::recursive_mutex &mutex = data->getExecutionLock();
    boost::lock_guard<boost::recursive_mutex> lock(mutex);

    if (setupComplete && fireUpdateEvents && !def->isHidden())
    {
        EventDefinitionPtr ed = getEventDefinition(E_FUNCTION_REMOVED);
        if (ed.get() != NULL)
        {
            std::map<FunctionDefinitionPtr, int>::iterator it = functionOder.find(def);
            if (it != functionOder.end())
            {
                functionOder.erase(it);
            }

            fireEvent(ed->getName(), def->getName());
        }
    }

    if (getContextManager() != NULL)
    {
        getContextManager()->functionRemoved(this, def);
    }
}

void AbstractContext::addEventDefinition(EventDefinitionPtr def)
{
    if (getEventDefinition(def->getName()) != NULL)
    {
        throw IllegalArgumentException("Event '" + def->getName() + "' already defined in context '" + getPath() + "'");
    }


    boost::lock_guard<boost::mutex> lock(eventDataLock);
    eventData[def->getName()] = EventDataPtr(new EventData(def));

    if (setupComplete && fireUpdateEvents && !def->isHidden())
    {
        EventDefinitionPtr ed = getEventDefinition(E_EVENT_ADDED);
        if (ed.get() != NULL)
        {
            eventOder[def] = eventCounter;
            eventCounter++;

            fireEvent(ed->getName(), DataTablePtr(new DataTable(evtDefToDataRecord(def, CallerControllerPtr()))));
        }
    }

    if (getContextManager() != NULL)
    {
        getContextManager()->eventAdded(this, def);
    }
}

void AbstractContext::removeEventDefinition(const AgString & name)
{
    removeEventDefinition(getEventDefinition(name));
}

void AbstractContext::removeEventDefinition(EventDefinitionPtr def)
{
    if (def.get() == NULL)
    {
        return;
    }

    boost::lock_guard<boost::mutex> lock(eventDataLock);

    std::map<AgString, EventDataPtr>::iterator it = eventData.find(def->getName());
    if (it != eventData.end())
    {
        eventData.erase(it);
        if (setupComplete && fireUpdateEvents && !def->isHidden())
        {
            EventDefinitionPtr ed = getEventDefinition(E_EVENT_REMOVED);
            if (ed.get() != NULL)
            {
                std::map<EventDefinitionPtr, int>::iterator it = eventOder.find(def);
                if (it != eventOder.end())
                {
                    eventOder.erase(it);
                }

                fireEvent(ed->getName(), def->getName());
            }
        }

        if (getContextManager() != NULL)
        {
            getContextManager()->eventRemoved( this, def);
        }
    }
}

VariableDataPtr AbstractContext::getVariableData(const AgString & name)
{
    boost::lock_guard<boost::mutex> lock(variableDataLock);
    std::map<AgString, VariableDataPtr>::iterator it = variableData.find(name);
    if (it != variableData.end())
        return it->second;
    else
        return VariableDataPtr();
}

VariableDefinitionPtr AbstractContext::getVariableDefinition(const AgString & name)
{
    VariableDataPtr data = getVariableData(name);
    return data.get() != NULL ? data->getDefinition() : VariableDefinitionPtr();
}

VariableDefinitionPtr AbstractContext::getVariableDefinition(const AgString & name, CallerControllerPtr caller)
{
    VariableDefinitionPtr def = getVariableDefinition(name);
    if (def.get() == NULL)
    {
        return VariableDefinitionPtr();
    }

    bool readAccessGranted = checkPermissions(def->getReadPermissions() != NULL ? def->getReadPermissions() : getPermissions(), caller, this);
    bool writeAccessGranted = checkPermissions(def->getWritePermissions() != NULL ? def->getWritePermissions() : getPermissions(), caller, this);

    return (readAccessGranted || writeAccessGranted) ? def : VariableDefinitionPtr();
}

FunctionDataPtr AbstractContext::getFunctionData(const AgString & name)
{
    FunctionDataPtr data = FunctionDataPtr();

    {
        boost::lock_guard<boost::mutex> lock(functionDataLock);
        std::map<AgString, FunctionDataPtr>::iterator it = functionData->find(name);
        if (it != functionData->end())
            data = it->second;
    }

    return data;
}

FunctionDefinitionPtr AbstractContext::getFunctionDefinition(const AgString & name)
{
    FunctionDataPtr data = getFunctionData(name);
    return data.get() != NULL ? data->getDefinition() : FunctionDefinitionPtr();
}

FunctionDefinitionPtr AbstractContext::getFunctionDefinition(const AgString & name, CallerControllerPtr caller)
{
    FunctionDefinitionPtr def = getFunctionDefinition(name);

    if (def.get() == NULL)
    {
        return FunctionDefinitionPtr();
    }

    bool accessGranted = checkPermissions(def->getPermissions().get() != NULL ? def->getPermissions() : getPermissions(), caller, this);
    return accessGranted ? def : FunctionDefinitionPtr();
}

EventDataPtr AbstractContext::getEventData(const AgString & name)
{
    // boost::lock_guard<boost::mutex> lock(eventDataLock);
    return eventData[name];
}

EventDefinitionPtr AbstractContext::getEventDefinition(const AgString & name)
{
    EventDataPtr ed = getEventData(name);
    return ed.get() != NULL ? ed->getDefinition() : EventDefinitionPtr();
}

EventDefinitionPtr AbstractContext::getEventDefinition(const AgString & name, CallerControllerPtr caller)
{
    EventDefinitionPtr def = getEventDefinition(name);

    if (def.get() == NULL)
    {
        return EventDefinitionPtr();
    }

    bool accessGranted = checkPermissions(def->getPermissions().get() != NULL ? def->getPermissions() : getPermissions(), caller, this);

    return accessGranted ? def : EventDefinitionPtr();
}

EventDefinitionPtr AbstractContext::getAndCheckEventDefinition(const AgString & name)
{
    setupEvents();

    EventDefinitionPtr def = getEventDefinition(name);

    if (def.get() == NULL)
    {
        throw ContextRuntimeException(MessageFormat::format(Cres::get()->getString("conEvtNotAvailExt"), name, getPath()));
    }

    return def;
}

void AbstractContext::setupEvents()
{
}

void AbstractContext::postEvent(EventPtr ev, EventDefinitionPtr ed, CallerControllerPtr caller, FireEventRequestControllerPtr request)
{
    UNUSED(ev);
    UNUSED(ed);
    UNUSED(caller);
    UNUSED(request);
}

void AbstractContext::updateEvent(EventPtr ev, EventDefinitionPtr ed, CallerControllerPtr caller, FireEventRequestControllerPtr request)
{
    UNUSED(ev);
    UNUSED(ed);
    UNUSED(caller);
    UNUSED(request);
}

EventPtr AbstractContext::fireEvent(
        EventDefinitionPtr ed,
        DataTablePtr data,
        int level, boost::optional<int64_t>  id,
        AgDatePtr creationtime,
        int  listener,
        CallerControllerPtr caller,
        FireEventRequestControllerPtr request,
        PermissionsPtr permissions)
{
    if (!id.is_initialized())
    {
        id = EventUtils::generateEventId();
    }

    EventPtr event = EventPtr(new Event(getPath(), ed, level == DEFAULT_EVENT_LEVEL ? ed->getLevel() : level, data, *id, creationtime, permissions));

    return fireEvent(ed, event, listener, caller, request);
}

EventPtr AbstractContext::fireEvent(EventPtr event)
{
    return fireEvent(getAndCheckEventDefinition(event->getName()), event, 0, CallerControllerPtr(), FireEventRequestControllerPtr());
}

EventPtr AbstractContext::fireEvent(EventDefinitionPtr ed, EventPtr event, int  listener, CallerControllerPtr caller, FireEventRequestControllerPtr request)
{
    if (caller.get() != NULL)
    {
        try
        {
            checkPermissions(ed->getFirePermissions().get() != NULL ? ed->getFirePermissions() : getPermissions(), caller);
        }
        catch (ContextSecurityException ex)
        {
            throw ContextRuntimeException(ex.getMessage(), ex.getDetails());
        }
    }

    EventProcessingRulePtr rule = getEventProcessingRule(event);

    ExpressionPtr prefilter = rule.get() != NULL ? rule->getPrefilterExpression() : ExpressionPtr();
    if (prefilter.get() != NULL)
    {
        try
        {
            EvaluatorPtr evaluator = EvaluatorPtr(new Evaluator(getContextManager(), this, event->getData(), getEventProcessingCallerController()));

            if (!evaluator->evaluateToBoolean(prefilter))
            {
                rule->addFiltered();

                if (Log::CONTEXT_EVENTS.isDebugEnabled())
                {
                    LOG_CONTEXT_EVENTS_DEBUG("Event '" + ed->toString().toUtf8() + "' in context '" + getPath().toUtf8() + "' was suppressed by pre-filter");
                }

                return EventPtr();
            }
        }
        catch (AggreGateException ex)
        {
            LOG_CONTEXT_EVENTS_INFO("Error processing pre-filter expression for event '" + ed->toString().toUtf8() + "' in context '" + getPath().toUtf8() + "': " + ex.getMessage().toUtf8());
        }
    }

    if (Log::CONTEXT_EVENTS.isDebugEnabled())
    {
        LOG_CONTEXT_EVENTS_DEBUG("Event '" + ed->toString().toUtf8() + "' fired in context '" + getPath().toUtf8() + "': " + event->toString().toUtf8());
    }

    event->setListener(listener);

    if (request.get() != NULL)
    {
        event->setOriginator(request->getOriginator());
    }

    EventDataPtr edata = getEventData(ed->getName());

    edata->registerFiredEvent();

    ExpressionPtr deduplicator = rule.get() != NULL ? rule->getDeduplicatorExpression() : ExpressionPtr();
    if (deduplicator.get() != NULL)
    {
        try
        {
            EvaluatorPtr evaluator = EvaluatorPtr(new Evaluator(getContextManager(), this, event->getData(), getEventProcessingCallerController()));

            AgString deduplicationId = evaluator->evaluateToString(deduplicator);

            event->setDeduplicationId(deduplicationId);
        }
        catch (AggreGateException ex)
        {
            LOG_CONTEXT_EVENTS_INFO("Error processing deduplicator expression for event '" + ed->toString().toUtf8() + "' in context '" + getPath().toUtf8() + "': " + ex.getMessage().toUtf8());
        }
    }

    if (event->getData()->isInvalid())
    {
        throw ContextRuntimeException(event->getData()->getInvalidationMessage());
    }

    if (ed->getFormat().get() != NULL)
    {
        AgString msg = event->getData()->conformMessage(ed->getFormat());

        if (!msg.empty())
        {
            LOG_CONTEXT_EVENTS_DEBUG("Wrong format data for event '" + ed->toString().toUtf8() + "' in context '" + toString().toUtf8() + "': " + msg.toUtf8());
            DataTablePtr newData = DataTablePtr(new DataTable(ed->getFormat(), true));
            DataTableReplication::copy(*event->getData(), newData);
            event->setData(newData);
        }
    }

    processBindings(event);

    processEnrichments(event, rule, caller);

    boost::posix_time::time_duration customExpirationPeriod;

    if (request.get() != NULL)
    {
        customExpirationPeriod = request->getCustomExpirationPeriod();
    }

    if (customExpirationPeriod.total_milliseconds() > 0)
    {
        boost::posix_time::ptime t = boost::posix_time::microsec_clock::local_time() + customExpirationPeriod;
        event->setExpirationtime(AgDatePtr(new AgDate(t)));
        // Otherwise event won't be persisted
    }
    else
    {
        int64_t userDefinedExpirationPeriod = rule.get() != NULL ? rule->getPeriod() : 0;
        if (userDefinedExpirationPeriod > 0)
        {
            boost::posix_time::ptime tt = boost::posix_time::microsec_clock::local_time() +  boost::posix_time::milliseconds(userDefinedExpirationPeriod);
            event->setExpirationtime(AgDatePtr(new AgDate( tt )));
        }
        // Otherwise event won't be persisted
    }

    int customMemoryStorageSize = rule.get() != NULL ? ((!rule->getDeduplicator().empty()) ? rule->getQueue() : 0) : 0;

    EventPtr processed = request.get() != NULL ? request->process(event) : event;

    if (processed.get() == NULL)
    {
        return EventPtr();
    }

    EventPtr duplicate = edata->store(processed, customMemoryStorageSize);

    try
    {
        if (duplicate.get() == NULL)
        {
            postEvent(event, ed, caller, request);

            if (rule.get() != NULL)
            {
                rule->addSaved();
            }
        }
        else
        {
            updateEvent(duplicate, ed, caller, request);

            if (rule.get() != NULL)
            {
                rule->addDuplicate();
            }
        }
    }
    catch (ContextException ex)
    {
        throw ContextRuntimeException(ex.getMessage(), ex.getDetails());
    }

    if (contextManager != NULL && (duplicate.get() == NULL || rule.get() == NULL || rule->isDuplicateDispatching()))
    {
        contextManager->queue(edata, event);
    }

    return event;
}

EventPtr AbstractContext::fireEvent(
        const AgString & name,
        int level,
        CallerControllerPtr caller,
        FireEventRequestControllerPtr request,
        PermissionsPtr permissions,
        DataTablePtr data)
{
    EventDefinitionPtr ed = getAndCheckEventDefinition(name);
    return fireEvent(ed, data, level, boost::optional<int64_t>(), AgDatePtr(), 0, caller, request, permissions);
}

EventPtr AbstractContext::fireEvent(
        const AgString & name,
        DataTablePtr data,
        int level,
        int64_t  id,
        AgDatePtr creationtime,
        int  listener,
        CallerControllerPtr caller,
        FireEventRequestControllerPtr request)
{
    return fireEvent(getAndCheckEventDefinition(name), data, level, id, creationtime, listener, caller, request, PermissionsPtr());
}

EventPtr AbstractContext::fireEvent(const AgString & name, ::DataTablePtr data)
{
    return fireEvent(getAndCheckEventDefinition(name), data, DEFAULT_EVENT_LEVEL, boost::optional<int64_t>(), AgDatePtr(), 0, CallerControllerPtr(), FireEventRequestControllerPtr(), PermissionsPtr());
}

EventPtr AbstractContext::fireEvent(const AgString & name, CallerControllerPtr caller, DataTablePtr data)
{
    return fireEvent(getAndCheckEventDefinition(name), data, DEFAULT_EVENT_LEVEL, boost::optional<int64_t>(), AgDatePtr(), 0, caller, FireEventRequestControllerPtr(), PermissionsPtr());
}

EventPtr AbstractContext::fireEvent(const AgString & name, int level, DataTablePtr data)
{
    return fireEvent(getAndCheckEventDefinition(name), data, level, boost::optional<int64_t>(), AgDatePtr(), 0, CallerControllerPtr(), FireEventRequestControllerPtr(), PermissionsPtr());
}

EventPtr AbstractContext::fireEvent(const AgString & name, int level, CallerControllerPtr caller, DataTablePtr data)
{
    return fireEvent(getAndCheckEventDefinition(name), data, level, boost::optional<int64_t>(), AgDatePtr(), 0, caller, FireEventRequestControllerPtr(), PermissionsPtr());
}

EventPtr AbstractContext::fireEvent(const AgString & name)
{
    EventDefinitionPtr ed = getAndCheckEventDefinition(name);
    return fireEvent(ed, DataTablePtr(new DataTable(ed->getFormat(), true)), DEFAULT_EVENT_LEVEL, boost::optional<int64_t>(), AgDatePtr(), 0, CallerControllerPtr(), FireEventRequestControllerPtr(), PermissionsPtr());
}

EventPtr AbstractContext::fireEvent(const AgString & name, CallerControllerPtr caller)
{
    EventDefinitionPtr ed = getAndCheckEventDefinition(name);
    return fireEvent(ed, DataTablePtr(new DataTable(ed->getFormat(), true)), DEFAULT_EVENT_LEVEL, boost::optional<int64_t>(), AgDatePtr(), 0, caller, FireEventRequestControllerPtr(), PermissionsPtr());
}

EventPtr AbstractContext::fireEvent(const AgString & name, std::list<AgObjectPtr> data)
{
    EventDefinitionPtr ed = getAndCheckEventDefinition(name);
    return fireEvent(ed, DataTablePtr(new DataTable(ed->getFormat(), data)), DEFAULT_EVENT_LEVEL, boost::optional<int64_t>(), AgDatePtr(), 0, CallerControllerPtr(), FireEventRequestControllerPtr(), PermissionsPtr());
}

EventPtr AbstractContext::fireEvent(const AgString &name, const AgString &data)
{
    std::list<AgObjectPtr> list;
    list.push_back(AgObjectPtr(new AgString(data)));
    return fireEvent(name, list);
}

EventProcessingRulePtr AbstractContext::getEventProcessingRule(EventPtr event)
{
    UNUSED(event);
    return EventProcessingRulePtr();
}


void AbstractContext::processBindings(EventPtr event)
{
    UNUSED(event);
}

CallerControllerPtr AbstractContext::getEventProcessingCallerController()
{
    return getContextManager()->getCallerController();
}

void AbstractContext::processEnrichments(EventPtr event, EventProcessingRulePtr rule, CallerControllerPtr caller)
{
    if (!rule.get() || !rule->getEnrichments().size())
    {
        return;
    }

    EvaluatorPtr evaluator = EvaluatorPtr(new Evaluator(getContextManager(), this, event->getData(), getEventProcessingCallerController()));
    std::list<EventEnrichmentRulePtr> &eerules = rule->getEnrichments();
    for (std::list<EventEnrichmentRulePtr>::iterator enrichmentRule = eerules.begin(); enrichmentRule != eerules.end(); ++enrichmentRule)
    {
        AgString name = (*enrichmentRule)->getName();
        try
        {
            AgString result = evaluator->evaluateToString((*enrichmentRule)->getEnrichmentExpression());

            if (result.empty())
            {
                continue;
            }

            event->addEnrichment(EnrichmentPtr(new Enrichment(name, result, AgDatePtr(new AgDate()), caller.get() != NULL ? caller->getUsername() : "")));
        }
        catch (AggreGateException ex)
        {
            LOG_CONTEXT_EVENTS_ERROR("Error adding enrichment '" + name.toUtf8() + "' to event '" + event->toString().toUtf8() + "': " + ex.getMessage().toUtf8());
        }
    }
}

void AbstractContext::lock(RequestControllerPtr request, boost::mutex lock)
{
    UNUSED(request);
    UNUSED(lock);
    //not used
    assert(0);
}

std::list<EventPtr>  AbstractContext::getEventHistory(const AgString & name)
{
    EventDataPtr ed = getEventData(name);

    if (ed.get() == NULL)
    {
        throw IllegalStateException(Cres::get()->getString("conEvtNotAvail") + name);
    }

    return ed->getHistory();
}

AgString AbstractContext::toString()
{
    AgString desc = getDescription();
    return desc.length() > 0 ? desc : getPath();
}

AgString AbstractContext::toDetailedString()
{
    AgString decription = getDescription();
    return decription.length() > 0 ? decription + " (" + getPath() + ")" : getPath();
}

void AbstractContext::accept(ContextVisitorPtr visitor)
{
    Context* ctx = this;
    if (visitor->shouldVisit(ctx))
    {
        visitor->visit(ctx);

        boost::lock_guard<boost::mutex> lock(childrenLock);
        for (std::vector<Context*>::iterator child = children.begin(); child != children.end(); ++child)
        {
            (*child)->accept(visitor);
        }
    }
}

EventDefinitionPtr AbstractContext::getChangeEventDefinition()
{
    return ED_CHANGE;
}

DataTablePtr AbstractContext::getVvariables(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr /*request*/)
{
    DataTablePtr ans = DataTablePtr(new DataTable(def->getFormat()));

    std::list<VariableDefinitionPtr> list = getVariableDefinitions(caller);

    for (std::list<VariableDefinitionPtr>::iterator vardef = list.begin(); vardef != list.end(); ++vardef)
    {
        if (variableOder.find(*vardef) == variableOder.end())
            ans->addRecord(varDefToDataRecord(*vardef, caller));
    }

    std::map<int, VariableDefinitionPtr> _variableOder;
    for (std::map<VariableDefinitionPtr, int>::iterator it = variableOder.begin(); it != variableOder.end(); ++it)
    {
        _variableOder[it->second] = it->first;
    }

    for (std::map<int, VariableDefinitionPtr>::reverse_iterator it = _variableOder.rbegin(); it != _variableOder.rend(); ++it)
    {
        ans->addRecord(varDefToDataRecord(it->second, caller));
    }

    return ans;
}


AgString AbstractContext::encodeFormat(TableFormatPtr format, CallerControllerPtr caller)
{
    UNUSED(caller);
    return format.get() != NULL ? format->encode(false) : "";
}

TableFormatPtr AbstractContext::decodeFormat(const AgString & source, CallerControllerPtr caller)
{
    UNUSED(caller);
    return !source.empty() ? TableFormatPtr(new TableFormat(source,
                                                            ClassicEncodingSettingsPtr(new ClassicEncodingSettings(false)))) : TableFormatPtr();
}

DataRecordPtr AbstractContext::varDefToDataRecord(VariableDefinitionPtr vd)
{
    return varDefToDataRecord(vd, CallerControllerPtr());
}

DataRecordPtr AbstractContext::varDefToDataRecord(VariableDefinitionPtr vd, CallerControllerPtr caller)
{
    DataRecord *dr = new DataRecord(VARIABLE_DEFINITION_FORMAT);
    dr->addString(vd->getName());
    dr->addString(encodeFormat(vd->getFormat(), caller));
    dr->addString(vd->getDescription());
    dr->addBoolean(vd->isReadable());
    dr->addBoolean(vd->isWritable());
    dr->addString(vd->getHelp());
    dr->addString(vd->getGroup());
    dr->addString(vd->getIconId());
    dr->addString(vd->getHelpId());
    dr->addLong(vd->getRemoteCacheTime().total_milliseconds());

    return DataRecordPtr(dr);
}

VariableDefinitionPtr AbstractContext::varDefFromDataRecord(DataRecordPtr rec)
{
    return varDefFromDataRecord(rec, CallerControllerPtr());
}

VariableDefinitionPtr AbstractContext::varDefFromDataRecord(DataRecordPtr rec, CallerControllerPtr caller)
{
    AgString variable = rec->getString(FIELD_VD_NAME);
    bool readable = rec->getBoolean(FIELD_VD_READABLE);
    bool writable = rec->getBoolean(FIELD_VD_WRITABLE);

    TableFormatPtr format;
    try
    {
        format = decodeFormat(rec->getString(FIELD_VD_FORMAT), caller);
    }
    catch (AggreGateException ex)
    {
        throw IllegalStateException("Error decoding format of variable '" + variable + "': " + ex.getMessage(), ex.getDetails());
    }

    VariableDefinitionPtr def = VariableDefinitionPtr(new VariableDefinition(variable, format, readable, writable,
                                                                             rec->getString(FIELD_VD_DESCRIPTION),
                                                                             rec->getString(FIELD_VD_GROUP)));

    def->setHelp(rec->getString(FIELD_VD_HELP));
    def->setIconId(rec->getString(FIELD_VD_ICON_ID));

    if (rec->hasField(FIELD_VD_HELP_ID))
    {
        def->setHelpId(rec->getString(FIELD_VD_HELP_ID));
    }

    if (rec->hasField(FIELD_VD_CACHE_TIME))
    {
        def->setRemoteCacheTime(boost::posix_time::milliseconds(rec->getLong(FIELD_VD_CACHE_TIME)));
    }

    return def;
}

DataTablePtr AbstractContext::getVfunctions(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr /*request*/)
{
    DataTablePtr ans = DataTablePtr(new DataTable(def->getFormat()));

    std::list<FunctionDefinitionPtr> list = getFunctionDefinitions(caller);
    for (std::list<FunctionDefinitionPtr>::iterator funcdef = list.begin(); funcdef != list.end(); ++funcdef)
    {
        if (functionOder.find(*funcdef) == functionOder.end())
            ans->addRecord(funcDefToDataRecord(*funcdef, caller));
    }

    std::map<int, FunctionDefinitionPtr> _functionOder;
    for (std::map<FunctionDefinitionPtr, int>::iterator it = functionOder.begin(); it != functionOder.end(); ++it)
    {
        _functionOder[it->second] = it->first;
    }

    for (std::map<int, FunctionDefinitionPtr>::reverse_iterator it = _functionOder.rbegin(); it != _functionOder.rend(); ++it)
    {
        ans->addRecord(funcDefToDataRecord(it->second, caller));
    }

    return ans;
}

DataRecordPtr AbstractContext::funcDefToDataRecord(FunctionDefinitionPtr fd)
{
    return funcDefToDataRecord(fd, CallerControllerPtr());
}

DataRecordPtr AbstractContext::funcDefToDataRecord(FunctionDefinitionPtr fd, CallerControllerPtr caller)
{
    DataRecord *dr = new DataRecord(FUNCTION_DEFINITION_FORMAT);
    dr->addString(fd->getName());
    dr->addString(encodeFormat(fd->getInputFormat(), caller));
    dr->addString(encodeFormat(fd->getOutputFormat(), caller));
    dr->addString(fd->getDescription());
    dr->addString(fd->getHelp());
    dr->addString(fd->getGroup());
    dr->addString(fd->getIconId());

    return DataRecordPtr(dr);
}


FunctionDefinitionPtr AbstractContext::funcDefFromDataRecord(DataRecordPtr rec)
{
    return funcDefFromDataRecord(rec, CallerControllerPtr());
}

FunctionDefinitionPtr AbstractContext::funcDefFromDataRecord(DataRecordPtr rec, CallerControllerPtr caller)
{
    AgString function = rec->getString(FIELD_FD_NAME);

    TableFormatPtr inputFormat;
    try
    {
        inputFormat = decodeFormat(rec->getString(FIELD_FD_INPUTFORMAT), caller);
    }
    catch (AggreGateException ex)
    {
        throw IllegalStateException("Error decoding input format of function '" + function + "': " + ex.getMessage(), ex.getDetails());
    }

    TableFormatPtr outputFormat;
    try
    {
        outputFormat = decodeFormat(rec->getString(FIELD_FD_OUTPUTFORMAT), caller);
    }
    catch (AggreGateException ex)
    {
        throw IllegalStateException("Error decoding output format of function '" + function + "': " + ex.getMessage(), ex.getDetails());
    }

    FunctionDefinitionPtr def = FunctionDefinitionPtr(new FunctionDefinition(function, inputFormat, outputFormat,
                                                                             rec->getString(FIELD_FD_DESCRIPTION),
                                                                             rec->getString(FIELD_FD_GROUP)));

    def->setHelp(rec->getString(FIELD_FD_HELP));
    def->setIconId(rec->getString(FIELD_FD_ICON_ID));
    return def;
}

DataTablePtr AbstractContext::getVevents(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr /*request*/)
{
    DataTablePtr ans = DataTablePtr(new DataTable(def->getFormat()));

    std::list<EventDefinitionPtr> list = getEventDefinitions(caller);
    for (std::list<EventDefinitionPtr>::iterator ed = list.begin(); ed != list.end(); ++ed)
    {
        if (eventOder.find(*ed) == eventOder.end())
            ans->addRecord(evtDefToDataRecord(*ed, caller));
    }

    std::map<int, EventDefinitionPtr> _eventOder;
    for (std::map<EventDefinitionPtr, int>::iterator it = eventOder.begin(); it != eventOder.end(); ++it)
    {
        _eventOder[it->second] = it->first;
    }

    for (std::map<int, EventDefinitionPtr>::reverse_iterator it = _eventOder.rbegin(); it != _eventOder.rend(); ++it)
    {
        ans->addRecord(evtDefToDataRecord(it->second, caller));
    }

    return ans;
}

DataRecordPtr AbstractContext::evtDefToDataRecord(EventDefinitionPtr ed)
{
    return evtDefToDataRecord(ed, CallerControllerPtr());
}

DataRecordPtr AbstractContext::evtDefToDataRecord(EventDefinitionPtr ed, CallerControllerPtr caller)
{
    DataRecord *dr = new DataRecord(EVENT_DEFINITION_FORMAT);

    dr->addString(ed->getName());
    dr->addString(encodeFormat(ed->getFormat(), caller));
    dr->addString(ed->getDescription());
    dr->addString(ed->getHelp());
    dr->addInt(ed->getLevel());
    dr->addString(ed->getGroup());
    dr->addString(ed->getIconId());

    return DataRecordPtr(dr);
}

EventDefinitionPtr AbstractContext::evtDefFromDataRecord(DataRecordPtr rec)
{
    return evtDefFromDataRecord(rec, CallerControllerPtr());
}

EventDefinitionPtr AbstractContext::evtDefFromDataRecord(DataRecordPtr rec, CallerControllerPtr caller)
{
    AgString event = rec->getString(FIELD_ED_NAME);

    TableFormatPtr format;
    try
    {
        format = decodeFormat(rec->getString(FIELD_ED_FORMAT), caller);
    }
    catch (AggreGateException ex)
    {
        throw IllegalStateException("Error decoding format of event '" + event + "': " + ex.getMessage(), ex.getDetails());
    }

    EventDefinitionPtr def = EventDefinitionPtr(new EventDefinition(event, format, rec->getString(FIELD_ED_DESCRIPTION),
                                                                    rec->getString(FIELD_ED_GROUP)));

    def->setLevel(rec->getInt(FIELD_ED_LEVEL));
    def->setHelp(rec->getString(FIELD_ED_HELP));
    def->setIconId(rec->getString(FIELD_ED_ICON_ID));
    return def;
}

DataTablePtr AbstractContext::getVactions(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request)
{
    UNUSED(request);

    DataTablePtr ans = DataTablePtr(new DataTable(def->getFormat()));

    std::list<ActionDefinitionPtr> list = getActionDefinitions(caller);
    for (std::list<ActionDefinitionPtr>::iterator adef = list.begin(); adef != list.end(); ++adef)
    {
        ans->addRecord(actDefToDataRecord(*adef));
    }

    return ans;
}

DataRecordPtr AbstractContext::actDefToDataRecord(ActionDefinitionPtr def)
{
    DataTablePtr resourceMasks = DataTablePtr(new DataTable(AbstractContext::RESOURCE_MASKS_FORMAT));

    //todo
    /*std::list<ResourceMaskPtr> list = def->getDropSources();
    for (std::list<ResourceMaskPtr>::iterator resourceMask = list.begin(); resourceMask != list.end(); ++resourceMask)
    {
        resourceMasks->addRecord()->addString((*resourceMask)->toString());
    }*/

    DataRecordPtr rec = DataRecordPtr(new DataRecord(AbstractContext::ACTION_DEF_FORMAT));
    rec->addString(def->getName());
    rec->addString(def->getDescription());
    rec->addString(def->getHelp());
    //todo - what is accelerator?
    assert(0);
    //rec->addString(def->getAccelerator() == null ? null : def.getAccelerator().toString());
    rec->addDataTable(resourceMasks);
    rec->addBoolean(def->isHidden());
    rec->addBoolean(def->isEnabled());
    rec->addString(def->getIconId());
    rec->addString(def->getGroup());
    rec->addString(def->getExecutionGroup().get() == NULL ? "" : def->getExecutionGroup()->toString());
    rec->addBoolean(def->isDefault());
    return rec;
}

void AbstractContext::executeTasks(std::list< boost::shared_ptr<Callable<int> > > tasks)
{
    try
    {
        //        if (isChildrenConcurrencyEnabled()) {
        //            getContextManager().getExecutorService().invokeAll(tasks);
        //        }else {
        for (std::list< boost::shared_ptr<Callable<int> > >::iterator child=tasks.begin(); child!=tasks.end(); ++child)
        {
            (*child)->call();
        }
        //        }
    }
    catch (AggreGateException ex) {
        throw IllegalStateException(ex.getMessage(), ex.getDetails());
    }
}

void AbstractContext::enableStatus()
{
    status = ContextStatusPtr(new ContextStatus());
}

ContextStatusPtr AbstractContext::getStatus()
{
    return status;
}

void AbstractContext::setStatus(int stat, const AgString & comment)
{
    bool statusChanged = (status->getStatus() != stat);
    bool commentChanged = (status->getComment() != comment);

    int oldStatus = status->getStatus();

    status->setStatus(stat);
    status->setComment(comment);

    if (statusChanged || commentChanged)
    {
        fireStatusChanged(stat, comment, oldStatus);
    }
}

void AbstractContext::fireStatusChanged(int /*status*/, const AgString & /*comment*/, int /*oldStatus*/)
{
}

void AbstractContext::enableVariableStatuses(bool /*persistent*/)
{
    // not used
    assert(0);
}


DataTablePtr AbstractContext::createVariableStatusesTable()
{
    boost::lock_guard<boost::mutex> lock(variableStatusesLock);

    DataTablePtr table = DataTablePtr(new DataTable(VFT_VARIABLE_STATUSES));
    std::map<AgString, VariableStatusPtr> statuses = getVariableStatuses();
    for (std::map<AgString, VariableStatusPtr>::iterator it = statuses.begin(); it != statuses.end(); ++it)
    {
        VariableStatusPtr vs = it->second;
        DataRecordPtr dr = table->addRecord();
        dr->addString(it->first);
        dr->addString(vs->getStatus());
        dr->addString(vs->getComment());
    }

    return table;
}


std::map<AgString, VariableStatusPtr> AbstractContext::getVariableStatuses()
{
    ensureVariableStatuses();
    return variableStatuses;
}

void AbstractContext::ensureVariableStatuses()
{
    if (variableStatuses.size() == 0)
    {
        DataTablePtr statuses = fetchVariableStatuses();
        for (std::vector<DataRecordPtr>::iterator rec = statuses->iteratorBegin(); rec != statuses->iteratorEnd(); ++rec)
        {
            variableStatuses[(*rec)->getString(VF_VARIABLE_STATUSES_NAME)] = VariableStatusPtr(new VariableStatus((*rec)->getString(VF_VARIABLE_STATUSES_STATUS), (*rec)->getString(VF_VARIABLE_STATUSES_COMMENT)));
        }
    }
}

DataTablePtr AbstractContext::fetchVariableStatuses()
{
    return DataTablePtr(new DataTable(VFT_VARIABLE_STATUSES));
}

void AbstractContext::updateVariableStatus(const AgString & variable, VariableStatusPtr status, bool persistent)
{
    VariableStatusPtr old;

    {
        boost::lock_guard<boost::mutex> lock(variableStatusesLock);
        ensureVariableStatuses();

        std::map<AgString, VariableStatusPtr>::iterator it = variableStatuses.find(variable);
        if (it != variableStatuses.end())
        {
            old = variableStatuses[variable];
        }
        variableStatuses[variable] = status;
    }

    if (old.get() == NULL || old->getStatus() != status->getStatus())
    {
        variableStatusesUpdated = true;

        std::list<AgObjectPtr> data;
        data.push_back( AgObjectPtr(new AgString( variable )) );
        data.push_back( AgObjectPtr(new AgString( status->getStatus() )) );
        data.push_back( AgObjectPtr(new AgString( status->getComment() )) );
        fireEvent(E_VARIABLE_STATUS_CHANGED, data);
    }

    if (persistent)
    {
        saveVariableStatuses();
    }
}

void AbstractContext::clearVariableStatuses()
{
    {
        boost::lock_guard<boost::mutex> lock(variableStatusesLock);
        variableStatuses.clear();
    }
    saveVariableStatuses();
}

void AbstractContext::saveVariableStatuses()
{
    if (variableStatusesUpdated)
    {
        persistVariableStatuses(createVariableStatusesTable());
    }
    variableStatusesUpdated = false;
}

void AbstractContext::persistVariableStatuses(DataTablePtr statuses)
{
    UNUSED(statuses);
    // Do nothing, statuses persistence may be supported by descendants
}


VariableStatusPtr AbstractContext::getVariableStatus(const AgString & name)
{
    boost::lock_guard<boost::mutex> lock(variableStatusesLock);
    std::map<AgString, VariableStatusPtr> m = getVariableStatuses();
    return m[name];

}

DataTablePtr AbstractContext::getVchildren(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request)
{
    UNUSED(request);
    DataTablePtr ans = DataTablePtr(new DataTable(def->getFormat()));
    std::list<Context*> list = getChildren(caller);
    for (std::list<Context*>::iterator con = list.begin(); con != list.end(); ++con)
    {
        ans->addRecord()->addString((*con)->getName());
    }
    return ans;
}

DataTablePtr AbstractContext::getVinfo(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request)
{
    UNUSED(def);
    UNUSED(caller);
    UNUSED(request);
    return createContextInfoTable();
}

DataTablePtr AbstractContext::createContextInfoTable()
{
    std::list<AgObjectPtr> firstRowData;
    firstRowData.push_back(AgObjectPtr(new AgString(getDescription())));
    firstRowData.push_back(AgObjectPtr(new AgString(getType())));
    firstRowData.push_back(AgObjectPtr(new AgString(getGroup())));
    firstRowData.push_back(AgObjectPtr(new AgString(getIconId())));
    firstRowData.push_back(AgObjectPtr(new AgString(getLocalRoot())));
    firstRowData.push_back(AgObjectPtr(new AgString()));
    firstRowData.push_back(AgObjectPtr(new AgString()));
    firstRowData.push_back(AgObjectPtr(new AgString(getRemoteRoot())));
    firstRowData.push_back(AgObjectPtr(new AgString(getRemotePath())));
    firstRowData.push_back(AgObjectPtr(new AgString(getRemotePrimaryRoot())));
    firstRowData.push_back(AgObjectPtr(new AgBoolean(isMapped())));

    return DataTablePtr(new DataTable(INFO_DEFINITION_FORMAT, firstRowData));
}

DataTablePtr AbstractContext::callFgetCopyData(FunctionDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr parameters)
{
    UNUSED(request);

    DataTablePtr result = DataTablePtr(new DataTable(TableFormatPtr(def->getOutputFormat()->clone())));

    AgString group = parameters->rec()->getString(VF_INFO_GROUP);

    std::list<Context*> recipients;

    DataTablePtr recipientsTable = parameters->rec()->getDataTable(FIF_COPY_DATA_RECIPIENTS);

    if (recipientsTable != NULL)
    {
        for (std::vector<DataRecordPtr>::iterator rec = recipientsTable->iteratorBegin(); rec != recipientsTable->iteratorEnd(); ++rec)
        {
            Context* recipient = getContextManager()->get((*rec)->getString(FIF_COPY_DATA_RECIPIENTS_RECIPIENT), caller);

            if (recipient != NULL)
            {
                recipients.push_back(recipient);
            }
        }
    }

    std::list<VariableDefinitionPtr> list = getVariableDefinitions(caller);
    for (std::list<VariableDefinitionPtr>::iterator vd = list.begin(); vd != list.end(); ++vd )
    {
        if (!group.empty() && ContextUtils::getBaseGroup((*vd)->getGroup()) != group)
        {
            continue;
        }

        if (group.empty() && (*vd)->getGroup().empty())
        {
            continue;
        }

        if (!(*vd)->isReadable())
        {
            continue;
        }

        if ((*vd)->getFormat().get() == NULL || !(*vd)->getFormat()->isReplicated())
        {
            continue;
        }

        if (recipients.size() != 0)
        {
            bool skip = true;

            for (std::list<Context *>::iterator recipient = recipients.begin(); recipient != recipients.end(); ++recipient)
            {
                VariableDefinitionPtr rvd = (*recipient)->getVariableDefinition((*vd)->getName());

                if (rvd.get() != NULL && rvd->isWritable() && (rvd->getFormat().get() == NULL || rvd->getFormat()->isReplicated()))
                {
                    skip = false;
                }
            }

            if (skip)
            {
                continue;
            }
        }

        DataTablePtr value = getVariable((*vd)->getName(), caller);

        TableFormatPtr format = TableFormatPtr(value->getFormat()->clone());

        DataTablePtr fields = DataTablePtr(new DataTable(FIFT_REPLICATE_FIELDS));

        for (std::vector<FieldFormat *>::iterator ff = format->getFields().begin(); ff != format->getFields().end(); ++ff)
        {
            if ((*ff)->isNotReplicated())
            {
                (*ff)->setReadonly(true);
            }

            if (!(*ff)->isHidden() && !(*ff)->isReadonly() && !(*ff)->isNotReplicated())
            {
                fields->addRecord()->addString((*ff)->getName()).addString((*ff)->toString()).addBoolean(true);
            }
        }

        result->addRecord()->addString((*vd)->getName()).addString((*vd)->getDescription()).addBoolean(false).addDataTable(fields).addDataTable(value);
    }

    result->fixRecords();

    return result;
}


DataTablePtr AbstractContext::callFcopy(FunctionDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr parameters)
{
    DataTablePtr result = DataTablePtr(new DataTable(def->getOutputFormat()));

    for (std::vector<DataRecordPtr>::iterator rec = parameters->iteratorBegin(); rec != parameters->iteratorEnd(); ++rec)
    {
        if (!(*rec)->getBoolean(FOF_COPY_DATA_REPLICATE))
        {
            continue;
        }

        AgString varName = (*rec)->getString(FOF_COPY_DATA_NAME);
        AgString providedDesc = (*rec)->getString(FOF_COPY_DATA_DESCRIPTION);
        DataTablePtr varValue = (*rec)->getDataTable(FOF_COPY_DATA_VALUE);

        VariableDefinitionPtr targetVd = getVariableDefinition(varName, caller);

        if (targetVd.get() == NULL)
        {
            result->addRecord()->addString(providedDesc).addBoolean(false).addString(Cres::get()->getString("conVarNotAvailInTgt"));
            continue;
        }

        AgString varDesc = targetVd->getDescription();

        if (!targetVd->isWritable())
        {
            result->addRecord()->addString(varDesc).addBoolean(false).addString(Cres::get()->getString("conVarNotWritableInTgt"));
            continue;
        }

        DataTablePtr tgtVal;

        try
        {
            tgtVal = getVariable(varName, caller);
        }
        catch (ContextException ex)
        {
            result->addRecord()->addString(varDesc).addBoolean(false).addString(Cres::get()->getString("conErrGettingTgtVar") + ex.getMessage());
            continue;
        }

        std::list<AgString> fields;

        DataTablePtr tt = (*rec)->getDataTable(FOF_COPY_DATA_FIELDS);

        for (std::vector<DataRecordPtr>::iterator fieldRec = tt->iteratorBegin(); fieldRec != tt->iteratorEnd(); ++fieldRec)
        {
            if ((*fieldRec)->getBoolean(FIF_REPLICATE_FIELDS_REPLICATE))
            {
                fields.push_back((*fieldRec)->getString(FIF_REPLICATE_FIELDS_NAME));
            }
        }

        std::set<AgString> tableCopyErrors = DataTableReplication::copy(*varValue, tgtVal, false, false, true, true, false, fields);

        DataTableUtils::inlineData(tgtVal, getContextManager(), caller);

        try
        {
            setVariable(targetVd, caller, request, tgtVal);
        }
        catch (ContextException ex)
        {
            LOG_CONTEXT_EVENTS_WARN("Error setting variable during context copy");
            result->addRecord()->addString(varDesc).addBoolean(false).addString(Cres::get()->getString("conErrSettingTgtVar") + ex.getMessage());
            continue;
        }

        if (tableCopyErrors.size() > 0)
        {
            result->addRecord()->addString(varDesc).addBoolean(false).addString(StringUtils::print(tableCopyErrors, "; "));
        }
        else
        {
            result->addRecord()->addString(varDesc).addBoolean(true);
        }
    }

    return result;
}

DataTablePtr AbstractContext::callFcopyToChildren(FunctionDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr parameters)
{
    std::list<Context*> cn = getChildren(caller);
    return copyTo(def, caller, request, parameters, cn);
}

DataTablePtr AbstractContext::copyTo(FunctionDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr parameters, std::list<Context*> &children)
{
    DataTablePtr result = DataTablePtr(new DataTable(def->getOutputFormat()));

    for (std::list<Context *>::iterator child = children.begin(); child != children.end(); ++child)
    {
        AgString conDesc = !(*child)->getDescription().empty() ? (*child)->getDescription() : (*child)->getPath();
        DataTablePtr conRes;

        try
        {
            conRes = (*child)->callFunction(F_COPY, caller, request, parameters);
        }
        catch (ContextException ex)
        {
            result->addRecord()->addString(conDesc).addString("").addBoolean(false).addString(ex.getMessage());
            continue;
        }

        for (std::vector<DataRecordPtr>::iterator rec = conRes->iteratorBegin(); rec != conRes->iteratorEnd(); ++rec)
        {
            result->addRecord()->addString(conDesc).addString((*rec)->getString(FIELD_REPLICATE_VARIABLE)).addBoolean((*rec)->getBoolean(FIELD_REPLICATE_SUCCESSFUL))
                    .addString((*rec)->getString(FIELD_REPLICATE_ERRORS));
        }
    }

    return result;
}
