/// <summary>
        /// Ctor.
        /// </summary>
        /// <param name="assignments">the list of variable assignments</param>
        /// <param name="variableService">variable service</param>
        /// <param name="eventAdapterService">event adapters</param>
        /// <throws><seealso cref="ExprValidationException" /> when variables cannot be found</throws>
        public VariableReadWritePackage(IList <OnTriggerSetAssignment> assignments, VariableService variableService, EventAdapterService eventAdapterService)

        {
            _metaData             = new VariableMetaData[assignments.Count];
            _readersForGlobalVars = new VariableReader[assignments.Count];
            _mustCoerce           = new bool[assignments.Count];
            _writers = new WriteDesc[assignments.Count];

            _variableTypes       = new Dictionary <String, Object>();
            _eventAdapterService = eventAdapterService;
            _variableService     = variableService;

            IDictionary <EventTypeSPI, CopyMethodDesc> eventTypeWrittenProps = new Dictionary <EventTypeSPI, CopyMethodDesc>();
            var count = 0;
            IList <VariableTriggerSetDesc> assignmentList = new List <VariableTriggerSetDesc>();

            foreach (var expressionWithAssignments in assignments)
            {
                var possibleVariableAssignment = ExprNodeUtility.CheckGetAssignmentToVariableOrProp(expressionWithAssignments.Expression);
                if (possibleVariableAssignment == null)
                {
                    throw new ExprValidationException("Missing variable assignment expression in assignment number " + count);
                }
                assignmentList.Add(new VariableTriggerSetDesc(possibleVariableAssignment.First, possibleVariableAssignment.Second.ExprEvaluator));

                var    fullVariableName = possibleVariableAssignment.First;
                var    variableName     = fullVariableName;
                String subPropertyName  = null;

                var indexOfDot = variableName.IndexOf('.');
                if (indexOfDot != -1)
                {
                    subPropertyName = variableName.Substring(indexOfDot + 1);
                    variableName    = variableName.Substring(0, indexOfDot);
                }

                VariableMetaData variableMetadata = variableService.GetVariableMetaData(variableName);
                _metaData[count] = variableMetadata;
                if (variableMetadata == null)
                {
                    throw new ExprValidationException("Variable by name '" + variableName + "' has not been created or configured");
                }
                if (variableMetadata.IsConstant)
                {
                    throw new ExprValidationException("Variable by name '" + variableName + "' is declared constant and may not be set");
                }
                if (variableMetadata.ContextPartitionName == null)
                {
                    _readersForGlobalVars[count] = variableService.GetReader(variableName, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID);
                }

                if (subPropertyName != null)
                {
                    if (variableMetadata.EventType == null)
                    {
                        throw new ExprValidationException("Variable by name '" + variableName + "' does not have a property named '" + subPropertyName + "'");
                    }
                    var type = variableMetadata.EventType;
                    if (!(type is EventTypeSPI))
                    {
                        throw new ExprValidationException("Variable by name '" + variableName + "' event type '" + type.Name + "' not writable");
                    }
                    var spi    = (EventTypeSPI)type;
                    var writer = spi.GetWriter(subPropertyName);
                    var getter = spi.GetGetter(subPropertyName);
                    if (writer == null)
                    {
                        throw new ExprValidationException("Variable by name '" + variableName + "' the property '" + subPropertyName + "' is not writable");
                    }

                    _variableTypes.Put(fullVariableName, spi.GetPropertyType(subPropertyName));
                    var writtenProps = eventTypeWrittenProps.Get(spi);
                    if (writtenProps == null)
                    {
                        writtenProps = new CopyMethodDesc(variableName, new List <String>());
                        eventTypeWrittenProps.Put(spi, writtenProps);
                    }
                    writtenProps.PropertiesCopied.Add(subPropertyName);

                    _writers[count] = new WriteDesc(spi, variableName, writer, getter);
                }
                else
                {
                    // determine types
                    var expressionType = possibleVariableAssignment.Second.ExprEvaluator.ReturnType;

                    if (variableMetadata.EventType != null)
                    {
                        if ((expressionType != null) && (!TypeHelper.IsSubclassOrImplementsInterface(expressionType, variableMetadata.EventType.UnderlyingType)))
                        {
                            throw new VariableValueException("Variable '" + variableName
                                                             + "' of declared event type '" + variableMetadata.EventType.Name + "' underlying type '" + variableMetadata.EventType.UnderlyingType.FullName +
                                                             "' cannot be assigned a value of type '" + expressionType.FullName + "'");
                        }
                        _variableTypes.Put(variableName, variableMetadata.EventType.UnderlyingType);
                    }
                    else
                    {
                        var variableType = variableMetadata.VariableType;
                        _variableTypes.Put(variableName, variableType);

                        // determine if the expression type can be assigned
                        if (variableType != typeof(object))
                        {
                            if ((TypeHelper.GetBoxedType(expressionType) != variableType) &&
                                (expressionType != null))
                            {
                                if ((!TypeHelper.IsNumeric(variableType)) ||
                                    (!TypeHelper.IsNumeric(expressionType)))
                                {
                                    throw new ExprValidationException(VariableServiceUtil.GetAssigmentExMessage(variableName, variableType, expressionType));
                                }

                                if (!(TypeHelper.CanCoerce(expressionType, variableType)))
                                {
                                    throw new ExprValidationException(VariableServiceUtil.GetAssigmentExMessage(variableName, variableType, expressionType));
                                }

                                _mustCoerce[count] = true;
                            }
                        }
                    }
                }

                count++;
            }

            _assignments = assignmentList.ToArray();

            if (eventTypeWrittenProps.IsEmpty())
            {
                _copyMethods = new Dictionary <EventTypeSPI, EventBeanCopyMethod>();
                return;
            }

            _copyMethods = new Dictionary <EventTypeSPI, EventBeanCopyMethod>();
            foreach (var entry in eventTypeWrittenProps)
            {
                var propsWritten = entry.Value.PropertiesCopied;
                var props        = propsWritten.ToArray();
                var copyMethod   = entry.Key.GetCopyMethod(props);
                if (copyMethod == null)
                {
                    throw new ExprValidationException("Variable '" + entry.Value.VariableName
                                                      + "' of declared type " + entry.Key.UnderlyingType.GetTypeNameFullyQualPretty() +
                                                      "' cannot be assigned to");
                }
                _copyMethods.Put(entry.Key, copyMethod);
            }
        }
        public VariableReadWritePackageForge(
            IList <OnTriggerSetAssignment> assignments,
            string statementName,
            StatementCompileTimeServices services)
        {
            _variables     = new VariableMetaData[assignments.Count];
            _mustCoerce    = new bool[assignments.Count];
            _writers       = new VariableTriggerWriteForge[assignments.Count];
            _variableTypes = new Dictionary <string, object>();

            IDictionary <EventTypeSPI, CopyMethodDesc> eventTypeWrittenProps = new Dictionary <EventTypeSPI, CopyMethodDesc>();
            var count = 0;
            IList <ExprAssignment> assignmentList = new List <ExprAssignment>();

            foreach (var spec in assignments)
            {
                var assignmentDesc = spec.Validated;
                assignmentList.Add(assignmentDesc);

                try {
                    if (assignmentDesc is ExprAssignmentStraight)
                    {
                        var assignment      = (ExprAssignmentStraight)assignmentDesc;
                        var identAssignment = assignment.Lhs;

                        var variableName     = identAssignment.Ident;
                        var variableMetadata = services.VariableCompileTimeResolver.Resolve(variableName);
                        if (variableMetadata == null)
                        {
                            throw new ExprValidationException("Variable by name '" + variableName + "' has not been created or configured");
                        }

                        _variables[count] = variableMetadata;
                        var expressionType = assignment.Rhs.Forge.EvaluationType;

                        if (assignment.Lhs is ExprAssignmentLHSIdent)
                        {
                            // determine types
                            if (variableMetadata.EventType != null)
                            {
                                if ((expressionType != null) &&
                                    (!TypeHelper.IsSubclassOrImplementsInterface(expressionType, variableMetadata.EventType.UnderlyingType)))
                                {
                                    throw new ExprValidationException(
                                              "Variable '" +
                                              variableName +
                                              "' of declared event type '" +
                                              variableMetadata.EventType.Name +
                                              "' underlying type '" +
                                              variableMetadata.EventType.UnderlyingType.Name +
                                              "' cannot be assigned a value of type '" +
                                              expressionType.Name +
                                              "'");
                                }

                                _variableTypes.Put(variableName, variableMetadata.EventType.UnderlyingType);
                            }
                            else
                            {
                                var variableType = variableMetadata.Type;
                                _variableTypes.Put(variableName, variableType);

                                // determine if the expression type can be assigned
                                if (variableType != typeof(object))
                                {
                                    if ((expressionType.GetBoxedType() != variableType.GetBoxedType()) &&
                                        (expressionType != null))
                                    {
                                        if ((!TypeHelper.IsNumeric(variableType)) ||
                                            (!TypeHelper.IsNumeric(expressionType)))
                                        {
                                            throw new ExprValidationException(VariableUtil.GetAssigmentExMessage(variableName, variableType, expressionType));
                                        }

                                        if (!(TypeHelper.CanCoerce(expressionType, variableType)))
                                        {
                                            throw new ExprValidationException(VariableUtil.GetAssigmentExMessage(variableName, variableType, expressionType));
                                        }

                                        _mustCoerce[count] = true;
                                    }
                                }
                            }
                        }
                        else if (assignment.Lhs is ExprAssignmentLHSIdentWSubprop)
                        {
                            var subpropAssignment = (ExprAssignmentLHSIdentWSubprop)assignment.Lhs;
                            var subPropertyName   = subpropAssignment.SubpropertyName;
                            if (variableMetadata.EventType == null)
                            {
                                throw new ExprValidationException(
                                          "Variable by name '" + variableName + "' does not have a property named '" + subPropertyName + "'");
                            }

                            var type = variableMetadata.EventType;
                            if (!(type is EventTypeSPI))
                            {
                                throw new ExprValidationException("Variable by name '" + variableName + "' event type '" + type.Name + "' not writable");
                            }

                            var spi        = (EventTypeSPI)type;
                            var writer     = spi.GetWriter(subPropertyName);
                            var getter     = spi.GetGetterSPI(subPropertyName);
                            var getterType = spi.GetPropertyType(subPropertyName);
                            if (writer == null)
                            {
                                throw new ExprValidationException(
                                          "Variable by name '" + variableName + "' the property '" + subPropertyName + "' is not writable");
                            }

                            var fullVariableName = variableName + "." + subPropertyName;
                            _variableTypes.Put(fullVariableName, spi.GetPropertyType(subPropertyName));
                            var writtenProps = eventTypeWrittenProps.Get(spi);
                            if (writtenProps == null)
                            {
                                writtenProps = new CopyMethodDesc(variableName, new List <string>());
                                eventTypeWrittenProps.Put(spi, writtenProps);
                            }

                            writtenProps.PropertiesCopied.Add(subPropertyName);

                            _writers[count] = new VariableTriggerWriteDescForge(
                                spi,
                                variableName,
                                writer,
                                getter,
                                getterType,
                                assignment.Rhs.Forge.EvaluationType);
                        }
                        else if (assignment.Lhs is ExprAssignmentLHSArrayElement)
                        {
                            var arrayAssign  = (ExprAssignmentLHSArrayElement)assignment.Lhs;
                            var variableType = variableMetadata.Type;
                            if (!variableType.IsArray)
                            {
                                throw new ExprValidationException("Variable '" + variableMetadata.VariableName + "' is not an array");
                            }

                            TypeWidenerSPI widener;
                            try {
                                widener = TypeWidenerFactory.GetCheckPropertyAssignType(
                                    ExprNodeUtilityPrint.ToExpressionStringMinPrecedenceSafe(assignment.Rhs),
                                    expressionType,
                                    variableType.GetElementType(),
                                    variableMetadata.VariableName,
                                    false,
                                    null,
                                    statementName);
                            }
                            catch (TypeWidenerException ex) {
                                throw new ExprValidationException(ex.Message, ex);
                            }

                            _writers[count] = new VariableTriggerWriteArrayElementForge(variableName, arrayAssign.IndexExpression.Forge, widener);
                        }
                        else
                        {
                            throw new IllegalStateException("Unrecognized left hand side assignment " + assignment.Lhs);
                        }
                    }
                    else if (assignmentDesc is ExprAssignmentCurly)
                    {
                        var curly = (ExprAssignmentCurly)assignmentDesc;
                        if (curly.Expression is ExprVariableNode)
                        {
                            throw new ExprValidationException("Missing variable assignment expression in assignment number " + count);
                        }

                        var variableVisitor = new ExprNodeVariableVisitor(services.VariableCompileTimeResolver);
                        curly.Expression.Accept(variableVisitor);
                        if (variableVisitor.VariableNames == null || variableVisitor.VariableNames.Count != 1)
                        {
                            throw new ExprValidationException("Assignment expression must receive a single variable value");
                        }

                        var variable = variableVisitor.VariableNames.First();
                        _variables[count] = variable.Value;
                        _writers[count]   = new VariableTriggerWriteCurlyForge(variable.Key, curly.Expression.Forge);
                    }
                    else
                    {
                        throw new IllegalStateException("Unrecognized assignment expression " + assignmentDesc);
                    }

                    if (_variables[count].IsConstant)
                    {
                        throw new ExprValidationException("Variable by name '" + _variables[count].VariableName + "' is declared constant and may not be set");
                    }

                    count++;
                }
                catch (ExprValidationException ex) {
                    throw new ExprValidationException(
                              "Failed to validate assignment expression '" +
                              ExprNodeUtilityPrint.ToExpressionStringMinPrecedenceSafe(assignmentDesc.OriginalExpression) +
                              "': " +
                              ex.Message,
                              ex);
                }
            }

            _assignments = assignmentList.ToArray();

            if (eventTypeWrittenProps.IsEmpty())
            {
                _copyMethods = EmptyDictionary <EventTypeSPI, EventBeanCopyMethodForge> .Instance;
                return;
            }

            _copyMethods = new Dictionary <EventTypeSPI, EventBeanCopyMethodForge>();
            foreach (var entry in eventTypeWrittenProps)
            {
                var propsWritten = entry.Value.PropertiesCopied;
                var props        = propsWritten.ToArray();
                var copyMethod   = entry.Key.GetCopyMethodForge(props);
                if (copyMethod == null)
                {
                    throw new ExprValidationException(
                              "Variable '" +
                              entry.Value.VariableName +
                              "' of declared type " +
                              entry.Key.UnderlyingType.CleanName() +
                              "' cannot be assigned to");
                }

                _copyMethods.Put(entry.Key, copyMethod);
            }
        }