예제 #1
0
        public void CheckAndWrite(String variableName, int agentInstanceId, Object newValue)
        {
            var metaData       = _variables.Get(variableName);
            var variableNumber = metaData.VariableNumber;

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

            var valueType = newValue.GetType();

            if (metaData.EventType != null)
            {
                if ((!TypeHelper.IsSubclassOrImplementsInterface(newValue.GetType(), metaData.EventType.UnderlyingType)))
                {
                    throw new VariableValueException("Variable '" + variableName
                                                     + "' of declared event type '" + metaData.EventType.Name + "' underlying type '" + metaData.EventType.UnderlyingType.FullName +
                                                     "' cannot be assigned a value of type '" + valueType.FullName + "'");
                }
                var eventBean = _eventAdapterService.AdapterForType(newValue, metaData.EventType);
                Write(variableNumber, agentInstanceId, eventBean);
                return;
            }

            var variableType = metaData.VariableType;

            if ((valueType == variableType) || (variableType == typeof(object)))
            {
                Write(variableNumber, agentInstanceId, newValue);
                return;
            }

            // Look for simple boxing rules
            var valueTypeBoxed    = valueType.GetBoxedType();
            var variableTypeBoxed = variableType.GetBoxedType();

            if (((valueType != valueTypeBoxed) || (variableType != variableTypeBoxed)) && ((valueTypeBoxed == variableTypeBoxed)))
            {
                Write(variableNumber, agentInstanceId, newValue);
                return;
            }

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

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

            object valueCoerced = CoercerFactory.CoerceBoxed(newValue, variableType);

            Write(variableNumber, agentInstanceId, valueCoerced);
        }
        /// <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);
            }
        }
예제 #3
0
        internal static FilterSpecParamForge HandleInSetNode(
            ExprInNode constituent,
            IDictionary <string, Pair <EventType, string> > taggedEventTypes,
            IDictionary <string, Pair <EventType, string> > arrayEventTypes,
            ISet <string> allTagNamesOrdered,
            StatementRawInfo raw,
            StatementCompileTimeServices services)
        {
            var left = constituent.ChildNodes[0];
            ExprFilterSpecLookupableForge lookupable = null;

            if (left is ExprFilterOptimizableNode)
            {
                var filterOptimizableNode = (ExprFilterOptimizableNode)left;
                lookupable = filterOptimizableNode.FilterLookupable;
            }
            else if (FilterSpecCompilerIndexPlannerHelper.HasLevelOrHint(FilterSpecCompilerIndexPlannerHint.LKUPCOMPOSITE, raw, services) &&
                     IsLimitedLookupableExpression(left))
            {
                lookupable = MakeLimitedLookupableForgeMayNull(left, raw, services);
            }

            if (lookupable == null)
            {
                return(null);
            }

            var op = FilterOperator.IN_LIST_OF_VALUES;

            if (constituent.IsNotIn)
            {
                op = FilterOperator.NOT_IN_LIST_OF_VALUES;
            }

            var expectedNumberOfConstants = constituent.ChildNodes.Length - 1;
            IList <FilterSpecParamInValueForge> listofValues = new List <FilterSpecParamInValueForge>();
            var it = Arrays.AsList(constituent.ChildNodes).GetEnumerator();

            it.MoveNext();             // ignore the first node as it's the identifier
            while (it.MoveNext())
            {
                var subNode = it.Current;
                if (subNode.Forge.ForgeConstantType.IsCompileTimeConstant)
                {
                    var constant = subNode.Forge.ExprEvaluator.Evaluate(null, true, null);
                    if (constant is ICollection <object> )
                    {
                        return(null);
                    }

                    if (constant is IDictionary <object, object> )
                    {
                        return(null);
                    }

                    if ((constant != null) && (constant is Array arrayConstant))
                    {
                        for (var i = 0; i < arrayConstant.Length; i++)
                        {
                            var arrayElement        = arrayConstant.GetValue(i);
                            var arrayElementCoerced = HandleConstantsCoercion(lookupable, arrayElement);
                            listofValues.Add(new FilterForEvalConstantAnyTypeForge(arrayElementCoerced));
                            if (i > 0)
                            {
                                expectedNumberOfConstants++;
                            }
                        }
                    }
                    else
                    {
                        constant = HandleConstantsCoercion(lookupable, constant);
                        listofValues.Add(new FilterForEvalConstantAnyTypeForge(constant));
                    }
                }
                else if (subNode is ExprContextPropertyNode)
                {
                    var     contextPropertyNode = (ExprContextPropertyNode)subNode;
                    var     returnType          = contextPropertyNode.Type;
                    Coercer coercer;
                    if (TypeHelper.IsCollectionMapOrArray(returnType))
                    {
                        CheckArrayCoercion(returnType, lookupable.ReturnType, lookupable.Expression);
                        coercer = null;
                    }
                    else
                    {
                        coercer = GetNumberCoercer(left.Forge.EvaluationType, contextPropertyNode.Type, lookupable.Expression);
                    }

                    var finalReturnType = coercer != null ? coercer.ReturnType : returnType;
                    listofValues.Add(new FilterForEvalContextPropForge(contextPropertyNode.PropertyName, contextPropertyNode.Getter, coercer, finalReturnType));
                }
                else if (subNode.Forge.ForgeConstantType.IsDeployTimeTimeConstant && subNode is ExprNodeDeployTimeConst)
                {
                    var     deployTimeConst = (ExprNodeDeployTimeConst)subNode;
                    var     returnType      = subNode.Forge.EvaluationType;
                    Coercer coercer;
                    if (TypeHelper.IsCollectionMapOrArray(returnType))
                    {
                        CheckArrayCoercion(returnType, lookupable.ReturnType, lookupable.Expression);
                        coercer = null;
                    }
                    else
                    {
                        coercer = GetNumberCoercer(left.Forge.EvaluationType, returnType, lookupable.Expression);
                    }

                    listofValues.Add(new FilterForEvalDeployTimeConstForge(deployTimeConst, coercer, returnType));
                }
                else if (subNode is ExprIdentNode)
                {
                    var identNodeInner = (ExprIdentNode)subNode;
                    if (identNodeInner.StreamId == 0)
                    {
                        break;                         // for same event evals use the boolean expression, via count compare failing below
                    }

                    var isMustCoerce    = false;
                    var coerceToType    = Boxing.GetBoxedType(lookupable.ReturnType);
                    var identReturnType = identNodeInner.Forge.EvaluationType;

                    if (TypeHelper.IsCollectionMapOrArray(identReturnType))
                    {
                        CheckArrayCoercion(identReturnType, lookupable.ReturnType, lookupable.Expression);
                        coerceToType = identReturnType;
                        // no action
                    }
                    else if (identReturnType != lookupable.ReturnType)
                    {
                        if (TypeHelper.IsNumeric(lookupable.ReturnType))
                        {
                            if (!TypeHelper.CanCoerce(identReturnType, lookupable.ReturnType))
                            {
                                ThrowConversionError(identReturnType, lookupable.ReturnType, lookupable.Expression);
                            }

                            isMustCoerce = true;
                        }
                        else
                        {
                            break;                             // assumed not compatible
                        }
                    }

                    FilterSpecParamInValueForge inValue;
                    var streamName = identNodeInner.ResolvedStreamName;
                    if (arrayEventTypes != null && !arrayEventTypes.IsEmpty() && arrayEventTypes.ContainsKey(streamName))
                    {
                        var indexAndProp   = GetStreamIndex(identNodeInner.ResolvedPropertyName);
                        var innerEventType = GetArrayInnerEventType(arrayEventTypes, streamName);
                        inValue = new FilterForEvalEventPropIndexedForge(
                            identNodeInner.ResolvedStreamName,
                            indexAndProp.First,
                            indexAndProp.Second,
                            innerEventType,
                            isMustCoerce,
                            coerceToType);
                    }
                    else
                    {
                        inValue = new FilterForEvalEventPropForge(
                            identNodeInner.ResolvedStreamName,
                            identNodeInner.ResolvedPropertyName,
                            identNodeInner.ExprEvaluatorIdent,
                            isMustCoerce,
                            coerceToType);
                    }

                    listofValues.Add(inValue);
                }
                else if (FilterSpecCompilerIndexPlannerHelper.HasLevelOrHint(FilterSpecCompilerIndexPlannerHint.VALUECOMPOSITE, raw, services) &&
                         IsLimitedValueExpression(subNode))
                {
                    var convertor      = GetMatchEventConvertor(subNode, taggedEventTypes, arrayEventTypes, allTagNamesOrdered);
                    var valueType      = subNode.Forge.EvaluationType;
                    var lookupableType = lookupable.ReturnType;
                    var numberCoercer  = GetNumberCoercer(lookupableType, valueType, lookupable.Expression);
                    var forge          = new FilterForEvalLimitedExprForge(subNode, convertor, numberCoercer);
                    listofValues.Add(forge);
                }
            }

            // Fallback if not all values in the in-node can be resolved to properties or constants
            if (listofValues.Count == expectedNumberOfConstants)
            {
                return(new FilterSpecParamInForge(lookupable, op, listofValues));
            }

            return(null);
        }
        public VariantPropertyDesc ResolveProperty(String propertyName, EventType[] variants)
        {
            bool existsInAll = true;
            Type commonType  = null;
            bool mustCoerce  = false;

            for (int i = 0; i < variants.Length; i++)
            {
                Type type = variants[i].GetPropertyType(propertyName); //.GetBoxedType();
                if (type == null)
                {
                    existsInAll = false;
                    continue;
                }

                if (commonType == null)
                {
                    commonType = type;
                    continue;
                }

                // compare types
                if (type == commonType)
                {
                    continue;
                }

                if (type.GetBoxedType() == commonType.GetBoxedType())
                {
                    commonType = commonType.GetBoxedType();
                    continue;
                }

                // coercion
                if (type.IsNumeric())
                {
                    if (TypeHelper.CanCoerce(type, commonType))
                    {
                        mustCoerce = true;
                        continue;
                    }
                    if (TypeHelper.CanCoerce(commonType, type))
                    {
                        mustCoerce = true;
                        commonType = type;
                    }
                }
                else if (commonType == typeof(Object))
                {
                    continue;
                }
                // common interface or base class
                else if (!type.IsBuiltinDataType())
                {
                    var supersForType = new FIFOHashSet <Type>();
                    TypeHelper.GetBase(type, supersForType);
                    supersForType.Remove(typeof(Object));

                    if (supersForType.Contains(commonType))
                    {
                        continue;   // type, or : common type
                    }
                    if (TypeHelper.IsSubclassOrImplementsInterface(commonType, type))
                    {
                        commonType = type;  // common type : type
                        continue;
                    }

                    // find common interface or type both implement
                    var supersForCommonType = new FIFOHashSet <Type>();
                    TypeHelper.GetBase(commonType, supersForCommonType);
                    supersForCommonType.Remove(typeof(Object));

                    // Take common classes first, ignoring interfaces
                    bool found = false;
                    foreach (Type superClassType in supersForType)
                    {
                        if (!superClassType.IsInterface && (supersForCommonType.Contains(superClassType)))
                        {
                            break;
                        }
                    }
                    if (found)
                    {
                        continue;
                    }
                    // Take common interfaces
                    foreach (var superClassType in supersForType)
                    {
                        if (superClassType.IsInterface && supersForCommonType.Contains(superClassType))
                        {
                            commonType = superClassType;
                            found      = true;
                            break;
                        }
                    }
                }

                commonType = typeof(Object);
            }

            if (!existsInAll)
            {
                return(null);
            }

            if (commonType == null)
            {
                return(null);
            }

            // property numbers should start at zero since the serve as array index
            var assignedPropertyNumber = currentPropertyNumber;

            currentPropertyNumber++;
            propertyGetterCache.AddGetters(assignedPropertyNumber, propertyName);

            EventPropertyGetter getter;

            if (mustCoerce)
            {
                SimpleTypeCaster caster = SimpleTypeCasterFactory.GetCaster(null, commonType);
                getter = new ProxyEventPropertyGetter
                {
                    ProcGet = eventBean =>
                    {
                        var variant        = (VariantEvent)eventBean;
                        var propertyGetter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType);
                        if (propertyGetter == null)
                        {
                            return(null);
                        }
                        var value = propertyGetter.Get(variant.UnderlyingEventBean);
                        if (value == null)
                        {
                            return(value);
                        }
                        return(caster.Invoke(value));
                    },
                    ProcGetFragment      = eventBean => null,
                    ProcIsExistsProperty = eventBean =>
                    {
                        var variant        = (VariantEvent)eventBean;
                        var propertyGetter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType);
                        if (propertyGetter == null)
                        {
                            return(false);
                        }
                        return(propertyGetter.IsExistsProperty(variant.UnderlyingEventBean));
                    }
                };
            }
            else
            {
                getter = new ProxyEventPropertyGetter
                {
                    ProcGet = eventBean =>
                    {
                        var variant        = (VariantEvent)eventBean;
                        var propertyGetter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType);
                        if (propertyGetter == null)
                        {
                            return(null);
                        }
                        return(propertyGetter.Get(variant.UnderlyingEventBean));
                    },
                    ProcGetFragment      = eventBean => null,
                    ProcIsExistsProperty = eventBean =>
                    {
                        var variant        = (VariantEvent)eventBean;
                        var propertyGetter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType);
                        if (propertyGetter == null)
                        {
                            return(false);
                        }
                        return(propertyGetter.IsExistsProperty(variant.UnderlyingEventBean));
                    }
                };
            }

            return(new VariantPropertyDesc(commonType, getter, true));
        }
        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);
            }
        }