public void CheckAndWrite(
            string deploymentId,
            string variableName,
            int agentInstanceId,
            object newValue)
        {
            var entry = DeploymentsWithVariables.Get(deploymentId);
            if (entry == null) {
                throw new ArgumentException("Failed to find variable deployment id '" + deploymentId + "'");
            }

            var variable = entry.GetVariable(variableName);
            var variableNumber = variable.VariableNumber;

            if (newValue == null) {
                Write(variableNumber, agentInstanceId, null);
                return;
            }

            var valueType = newValue.GetType();

            if (variable.MetaData.EventType != null) {
                if (!TypeHelper.IsSubclassOrImplementsInterface(
                    newValue.GetType(),
                    variable.MetaData.EventType.UnderlyingType)) {
                    throw new VariableValueException(
                        "Variable '" +
                        variableName +
                        "' of declared event type '" +
                        variable.MetaData.EventType.Name +
                        "' underlying type '" +
                        variable.MetaData.EventType.UnderlyingType.CleanName() +
                        "' cannot be assigned a value of type '" +
                        valueType.Name +
                        "'");
                }

                var eventBean = eventBeanTypedEventFactory.AdapterForTypedObject(newValue, variable.MetaData.EventType);
                Write(variableNumber, agentInstanceId, eventBean);
                return;
            }

            var variableType = variable.MetaData.Type;
            if (valueType.Equals(variableType) || variableType == typeof(object)) {
                Write(variableNumber, agentInstanceId, newValue);
                return;
            }

            if (TypeHelper.IsSubclassOrImplementsInterface(valueType, variableType)) {
                Write(variableNumber, agentInstanceId, newValue);
                return;
            }

            if (!variableType.IsNumeric() ||
                !valueType.IsNumeric()) {
                throw new VariableValueException(
                    VariableUtil.GetAssigmentExMessage(variableName, variableType, valueType));
            }

            // determine if the expression type can be assigned
            if (!valueType.CanCoerce(variableType)) {
                throw new VariableValueException(
                    VariableUtil.GetAssigmentExMessage(variableName, variableType, valueType));
            }

            var valueCoerced = TypeHelper.CoerceBoxed(newValue, variableType);
            Write(variableNumber, agentInstanceId, valueCoerced);
        }
        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);
            }
        }