/// <summary> /// Makes the property evaluator. /// </summary> /// <param name="spec">is the property specification</param> /// <param name="sourceEventType">the event type</param> /// <param name="optionalSourceStreamName">the source stream name</param> /// <param name="eventAdapterService">for event instances</param> /// <param name="engineImportService">The engine import service.</param> /// <param name="timeProvider">provides time</param> /// <param name="variableService">for resolving variables</param> /// <param name="scriptingService">The scripting service.</param> /// <param name="tableService">The table service.</param> /// <param name="engineURI">engine URI</param> /// <param name="statementId">The statement identifier.</param> /// <param name="statementName">Name of the statement.</param> /// <param name="annotations">The annotations.</param> /// <param name="assignedTypeNumberStack">The assigned type number stack.</param> /// <param name="configuration">The configuration.</param> /// <param name="namedWindowMgmtService">The named window service.</param> /// <param name="statementExtensionSvcContext">The statement extension SVC context.</param> /// <returns> /// propert evaluator /// </returns> /// <exception cref="ExprValidationException">Missing @type(name) declaration providing the event type name of the return type for expression ' + /// ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + ' /// or /// Event type by name ' + atom.OptionalResultEventType + ' could not be found /// or /// Event type ' + streamEventType.Name + ' underlying type + streamEventType.UnderlyingType.Name + /// cannot be assigned a value of type + returnType.GetTypeNameFullyQualPretty() /// or /// Return type of expression ' + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + ' is ' + returnType.Name + ', expected an Iterable or array result /// or /// Property rename ' + rawStreamSpec.MaskTypeName + ' not found in path /// or /// Expression in a property-selection may not utilize + isMinimal</exception> /// <exception cref="IllegalStateException">Unknown select clause item: + raw</exception> /// <throws>ExprValidationException if any expressions could not be verified</throws> public static PropertyEvaluator MakeEvaluator( PropertyEvalSpec spec, EventType sourceEventType, string optionalSourceStreamName, EventAdapterService eventAdapterService, EngineImportService engineImportService, TimeProvider timeProvider, VariableService variableService, ScriptingService scriptingService, TableService tableService, string engineURI, int statementId, string statementName, Attribute[] annotations, ICollection <int> assignedTypeNumberStack, ConfigurationInformation configuration, NamedWindowMgmtService namedWindowMgmtService, StatementExtensionSvcContext statementExtensionSvcContext) { var length = spec.Atoms.Count; var containedEventEvals = new ContainedEventEval[length]; var fragmentEventTypes = new FragmentEventType[length]; var currentEventType = sourceEventType; var whereClauses = new ExprEvaluator[length]; var streamEventTypes = new List <EventType>(); var streamNames = new List <string>(); var streamNameAndNumber = new Dictionary <string, int>().WithNullSupport(); var expressionTexts = new List <string>(); var validateContext = new ExprEvaluatorContextTimeOnly(timeProvider); streamEventTypes.Add(sourceEventType); streamNames.Add(optionalSourceStreamName); streamNameAndNumber.Put(optionalSourceStreamName, 0); expressionTexts.Add(sourceEventType.Name); IList <SelectClauseElementCompiled> cumulativeSelectClause = new List <SelectClauseElementCompiled>(); for (var i = 0; i < length; i++) { var atom = spec.Atoms[i]; ContainedEventEval containedEventEval = null; string expressionText = null; EventType streamEventType = null; FragmentEventType fragmentEventType = null; // Resolve directly as fragment event type if possible if (atom.SplitterExpression is ExprIdentNode) { var propertyName = ((ExprIdentNode)atom.SplitterExpression).FullUnresolvedName; fragmentEventType = currentEventType.GetFragmentType(propertyName); if (fragmentEventType != null) { var getter = currentEventType.GetGetter(propertyName); if (getter != null) { containedEventEval = new ContainedEventEvalGetter(getter); expressionText = propertyName; streamEventType = fragmentEventType.FragmentType; } } } // evaluate splitter expression if (containedEventEval == null) { ExprNodeUtility.ValidatePlainExpression( ExprNodeOrigin.CONTAINEDEVENT, ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression), atom.SplitterExpression); var availableTypes = streamEventTypes.ToArray(); var availableStreamNames = streamNames.ToArray(); var isIStreamOnly = new bool[streamNames.Count]; isIStreamOnly.Fill(true); StreamTypeService streamTypeService = new StreamTypeServiceImpl( availableTypes, availableStreamNames, isIStreamOnly, engineURI, false); var validationContext = new ExprValidationContext( streamTypeService, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, validateContext, eventAdapterService, statementName, statementId, annotations, null, scriptingService, false, false, true, false, null, false); var validatedExprNode = ExprNodeUtility.GetValidatedSubtree( ExprNodeOrigin.CONTAINEDEVENT, atom.SplitterExpression, validationContext); var evaluator = validatedExprNode.ExprEvaluator; // determine result type if (atom.OptionalResultEventType == null) { throw new ExprValidationException( "Missing @type(name) declaration providing the event type name of the return type for expression '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + "'"); } streamEventType = eventAdapterService.GetEventTypeByName(atom.OptionalResultEventType); if (streamEventType == null) { throw new ExprValidationException( "Event type by name '" + atom.OptionalResultEventType + "' could not be found"); } var returnType = evaluator.ReturnType; // when the expression returns an array, allow array values to become the column of the single-column event type if (returnType.IsArray && streamEventType.PropertyNames.Length == 1 && TypeHelper.IsSubclassOrImplementsInterface( TypeHelper.GetBoxedType(returnType.GetElementType()), TypeHelper.GetBoxedType(streamEventType.GetPropertyType(streamEventType.PropertyNames[0])))) { var writables = eventAdapterService.GetWriteableProperties(streamEventType, false); if (!writables.IsEmpty()) { try { EventBeanManufacturer manufacturer = EventAdapterServiceHelper.GetManufacturer( eventAdapterService, streamEventType, new WriteablePropertyDescriptor[] { writables.First() }, engineImportService, false, eventAdapterService.EventAdapterAvroHandler); containedEventEval = new ContainedEventEvalArrayToEvent(evaluator, manufacturer); } catch (EventBeanManufactureException e) { throw new ExprValidationException( "Event type '" + streamEventType.Name + "' cannot be populated: " + e.Message, e); } } else { throw new ExprValidationException("Event type '" + streamEventType.Name + "' cannot be written to"); } } else if (returnType.IsArray() && returnType.GetElementType() == typeof(EventBean)) { containedEventEval = new ContainedEventEvalEventBeanArray(evaluator); } else { EventBeanFactory eventBeanFactory = EventAdapterServiceHelper.GetFactoryForType( streamEventType, eventAdapterService); // check expression result type against eventtype expected underlying type if (returnType.IsArray()) { if (!TypeHelper.IsSubclassOrImplementsInterface(returnType.GetElementType(), streamEventType.UnderlyingType)) { throw new ExprValidationException( "Event type '" + streamEventType.Name + "' underlying type " + streamEventType.UnderlyingType.FullName + " cannot be assigned a value of type " + TypeHelper.GetTypeNameFullyQualPretty(returnType)); } } else if (GenericExtensions.IsGenericEnumerable(returnType) || returnType.IsImplementsInterface <IEnumerable>()) { // fine, assumed to return the right type } else { throw new ExprValidationException( "Return type of expression '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + "' is '" + returnType.FullName + "', expected an Iterable or array result"); } containedEventEval = new ContainedEventEvalExprNode(evaluator, eventBeanFactory); } expressionText = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(validatedExprNode); fragmentEventType = new FragmentEventType(streamEventType, true, false); } // validate where clause, if any streamEventTypes.Add(streamEventType); streamNames.Add(atom.OptionalAsName); streamNameAndNumber.Put(atom.OptionalAsName, i + 1); expressionTexts.Add(expressionText); if (atom.OptionalWhereClause != null) { var whereTypes = streamEventTypes.ToArray(); var whereStreamNames = streamNames.ToArray(); var isIStreamOnly = new bool[streamNames.Count]; isIStreamOnly.Fill(true); StreamTypeService streamTypeService = new StreamTypeServiceImpl( whereTypes, whereStreamNames, isIStreamOnly, engineURI, false); var validationContext = new ExprValidationContext( streamTypeService, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, validateContext, eventAdapterService, statementName, statementId, annotations, null, scriptingService, false, false, true, false, null, false); whereClauses[i] = ExprNodeUtility.GetValidatedSubtree( ExprNodeOrigin.CONTAINEDEVENT, atom.OptionalWhereClause, validationContext).ExprEvaluator; } // validate select clause if (atom.OptionalSelectClause != null) { var whereTypes = streamEventTypes.ToArray(); var whereStreamNames = streamNames.ToArray(); var isIStreamOnly = new bool[streamNames.Count]; isIStreamOnly.Fill(true); StreamTypeService streamTypeService = new StreamTypeServiceImpl( whereTypes, whereStreamNames, isIStreamOnly, engineURI, false); var validationContext = new ExprValidationContext( streamTypeService, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, validateContext, eventAdapterService, statementName, statementId, annotations, null, scriptingService, false, false, true, false, null, false); foreach (var raw in atom.OptionalSelectClause.SelectExprList) { if (raw is SelectClauseStreamRawSpec) { var rawStreamSpec = (SelectClauseStreamRawSpec)raw; if (!streamNames.Contains(rawStreamSpec.StreamName)) { throw new ExprValidationException( "Property rename '" + rawStreamSpec.StreamName + "' not found in path"); } var streamSpec = new SelectClauseStreamCompiledSpec( rawStreamSpec.StreamName, rawStreamSpec.OptionalAsName); int streamNumber = streamNameAndNumber.Get(rawStreamSpec.StreamName); streamSpec.StreamNumber = streamNumber; cumulativeSelectClause.Add(streamSpec); } else if (raw is SelectClauseExprRawSpec) { var exprSpec = (SelectClauseExprRawSpec)raw; var exprCompiled = ExprNodeUtility.GetValidatedSubtree( ExprNodeOrigin.CONTAINEDEVENT, exprSpec.SelectExpression, validationContext); var resultName = exprSpec.OptionalAsName; if (resultName == null) { resultName = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(exprCompiled); } cumulativeSelectClause.Add( new SelectClauseExprCompiledSpec( exprCompiled, resultName, exprSpec.OptionalAsName, exprSpec.IsEvents)); var isMinimal = ExprNodeUtility.IsMinimalExpression(exprCompiled); if (isMinimal != null) { throw new ExprValidationException( "Expression in a property-selection may not utilize " + isMinimal); } } else if (raw is SelectClauseElementWildcard) { // wildcards are stream selects: we assign a stream name (any) and add a stream wildcard select var streamNameAtom = atom.OptionalAsName; if (streamNameAtom == null) { streamNameAtom = UuidGenerator.Generate(); } var streamSpec = new SelectClauseStreamCompiledSpec(streamNameAtom, atom.OptionalAsName); var streamNumber = i + 1; streamSpec.StreamNumber = streamNumber; cumulativeSelectClause.Add(streamSpec); } else { throw new IllegalStateException("Unknown select clause item:" + raw); } } } currentEventType = fragmentEventType.FragmentType; fragmentEventTypes[i] = fragmentEventType; containedEventEvals[i] = containedEventEval; } if (cumulativeSelectClause.IsEmpty()) { if (length == 1) { return(new PropertyEvaluatorSimple( containedEventEvals[0], fragmentEventTypes[0], whereClauses[0], expressionTexts[0])); } else { return(new PropertyEvaluatorNested(containedEventEvals, fragmentEventTypes, whereClauses, expressionTexts)); } } else { var accumulative = new PropertyEvaluatorAccumulative( containedEventEvals, fragmentEventTypes, whereClauses, expressionTexts); var whereTypes = streamEventTypes.ToArray(); var whereStreamNames = streamNames.ToArray(); var isIStreamOnly = new bool[streamNames.Count]; isIStreamOnly.Fill(true); StreamTypeService streamTypeService = new StreamTypeServiceImpl( whereTypes, whereStreamNames, isIStreamOnly, engineURI, false); var cumulativeSelectArr = cumulativeSelectClause.ToArray(); var selectExpr = SelectExprProcessorFactory.GetProcessor( assignedTypeNumberStack, cumulativeSelectArr, false, null, null, null, streamTypeService, eventAdapterService, null, null, null, engineImportService, validateContext, variableService, scriptingService, tableService, timeProvider, engineURI, statementId, statementName, annotations, null, configuration, null, namedWindowMgmtService, null, null, statementExtensionSvcContext); return(new PropertyEvaluatorSelect(selectExpr, accumulative)); } }
public static PropertyEvaluatorForge MakeEvaluator( PropertyEvalSpec spec, EventType sourceEventType, string optionalSourceStreamName, StatementRawInfo rawInfo, StatementCompileTimeServices services) { var length = spec.Atoms.Count; var containedEventForges = new ContainedEventEvalForge[length]; var fragmentEventTypes = new FragmentEventType[length]; var currentEventType = sourceEventType; var whereClauses = new ExprForge[length]; IList<EventType> streamEventTypes = new List<EventType>(); IList<string> streamNames = new List<string>(); IDictionary<string, int> streamNameAndNumber = new Dictionary<string, int>().WithNullKeySupport(); IList<string> expressionTexts = new List<string>(); streamEventTypes.Add(sourceEventType); streamNames.Add(optionalSourceStreamName); streamNameAndNumber.Put(optionalSourceStreamName, 0); expressionTexts.Add(sourceEventType.Name); IList<SelectClauseElementCompiled> cumulativeSelectClause = new List<SelectClauseElementCompiled>(); for (var i = 0; i < length; i++) { var atom = spec.Atoms[i]; ContainedEventEvalForge containedEventEval = null; string expressionText = null; EventType streamEventType = null; FragmentEventType fragmentEventType = null; // Resolve directly as fragment event type if possible if (atom.SplitterExpression is ExprIdentNode) { var propertyName = ((ExprIdentNode) atom.SplitterExpression).FullUnresolvedName; fragmentEventType = currentEventType.GetFragmentType(propertyName); if (fragmentEventType != null) { var getter = ((EventTypeSPI) currentEventType).GetGetterSPI(propertyName); if (getter != null) { containedEventEval = new ContainedEventEvalGetterForge(getter); expressionText = propertyName; streamEventType = fragmentEventType.FragmentType; } } } // evaluate splitter expression if (containedEventEval == null) { ExprNodeUtilityValidate.ValidatePlainExpression( ExprNodeOrigin.CONTAINEDEVENT, atom.SplitterExpression); var availableTypes = streamEventTypes.ToArray(); var availableStreamNames = streamNames.ToArray(); var isIStreamOnly = new bool[streamNames.Count]; isIStreamOnly.Fill(true); StreamTypeService streamTypeService = new StreamTypeServiceImpl( availableTypes, availableStreamNames, isIStreamOnly, false, false); var validationContext = new ExprValidationContextBuilder(streamTypeService, rawInfo, services) .WithAllowBindingConsumption(true) .Build(); var validatedExprNode = ExprNodeUtilityValidate.GetValidatedSubtree( ExprNodeOrigin.CONTAINEDEVENT, atom.SplitterExpression, validationContext); // determine result type if (atom.OptionalResultEventType == null) { throw new ExprValidationException( "Missing @type(name) declaration providing the event type name of the return type for expression '" + ExprNodeUtilityPrint.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + "'"); } streamEventType = services.EventTypeCompileTimeResolver.GetTypeByName(atom.OptionalResultEventType); if (streamEventType == null) { throw new ExprValidationException( "Event type by name '" + atom.OptionalResultEventType + "' could not be found"); } var returnType = validatedExprNode.Forge.EvaluationType; // when the expression returns an array, allow array values to become the column of the single-column event type if (returnType.IsArray && streamEventType.PropertyNames.Length == 1 && !(streamEventType is JsonEventType) && // since json string-array should not become itself the property TypeHelper.IsSubclassOrImplementsInterface( returnType.GetElementType().GetBoxedType(), streamEventType.GetPropertyType(streamEventType.PropertyNames[0]).GetBoxedType())) { var writables = EventTypeUtility.GetWriteableProperties(streamEventType, false, false); if (writables != null && !writables.IsEmpty()) { try { var manufacturer = EventTypeUtility.GetManufacturer( streamEventType, new[] {writables.First()}, services.ImportServiceCompileTime, false, services.EventTypeAvroHandler); containedEventEval = new ContainedEventEvalArrayToEventForge( validatedExprNode.Forge, manufacturer); } catch (EventBeanManufactureException e) { throw new ExprValidationException( "Event type '" + streamEventType.Name + "' cannot be populated: " + e.Message, e); } } else { throw new ExprValidationException( "Event type '" + streamEventType.Name + "' cannot be written to"); } } else if (returnType.IsArray && returnType.GetElementType() == typeof(EventBean)) { containedEventEval = new ContainedEventEvalEventBeanArrayForge(validatedExprNode.Forge); } else { // check expression result type against eventtype expected underlying type if (returnType.IsArray) { if (!(streamEventType is JsonEventType)) { if (!TypeHelper.IsSubclassOrImplementsInterface( returnType.GetElementType(), streamEventType.UnderlyingType)) { throw new ExprValidationException( "Event type '" + streamEventType.Name + "' underlying type " + streamEventType.UnderlyingType.CleanName() + " cannot be assigned a value of type " + returnType.CleanName()); } } else { if (returnType.GetElementType() != typeof(string)) { throw new ExprValidationException( "Event type '" + streamEventType.Name + "' requires string-type array and cannot be assigned from value of type " + returnType.CleanName()); } } } else if (GenericExtensions.IsGenericEnumerable(returnType) || TypeHelper.IsImplementsInterface<System.Collections.IEnumerable>(returnType)) { // fine, assumed to return the right type } else { throw new ExprValidationException( "Return type of expression '" + ExprNodeUtilityPrint.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + "' is '" + returnType.Name + "', expected an Iterable or array result"); } containedEventEval = new ContainedEventEvalExprNodeForge( validatedExprNode.Forge, streamEventType); } expressionText = ExprNodeUtilityPrint.ToExpressionStringMinPrecedenceSafe(validatedExprNode); fragmentEventType = new FragmentEventType(streamEventType, true, false); } // validate where clause, if any streamEventTypes.Add(streamEventType); streamNames.Add(atom.OptionalAsName); streamNameAndNumber.Put(atom.OptionalAsName, i + 1); expressionTexts.Add(expressionText); if (atom.OptionalWhereClause != null) { var whereTypes = streamEventTypes.ToArray(); var whereStreamNames = streamNames.ToArray(); var isIStreamOnly = new bool[streamNames.Count]; isIStreamOnly.Fill(true); StreamTypeService streamTypeService = new StreamTypeServiceImpl( whereTypes, whereStreamNames, isIStreamOnly, false, false); var validationContext = new ExprValidationContextBuilder(streamTypeService, rawInfo, services) .WithAllowBindingConsumption(true) .Build(); var whereClause = ExprNodeUtilityValidate.GetValidatedSubtree( ExprNodeOrigin.CONTAINEDEVENT, atom.OptionalWhereClause, validationContext); whereClauses[i] = whereClause.Forge; } // validate select clause if (atom.OptionalSelectClause != null && !atom.OptionalSelectClause.SelectExprList.IsEmpty()) { var whereTypes = streamEventTypes.ToArray(); var whereStreamNames = streamNames.ToArray(); var isIStreamOnly = new bool[streamNames.Count]; isIStreamOnly.Fill(true); StreamTypeService streamTypeService = new StreamTypeServiceImpl( whereTypes, whereStreamNames, isIStreamOnly, false, false); var validationContext = new ExprValidationContextBuilder(streamTypeService, rawInfo, services) .WithAllowBindingConsumption(true) .Build(); foreach (var raw in atom.OptionalSelectClause.SelectExprList) { if (raw is SelectClauseStreamRawSpec) { var rawStreamSpec = (SelectClauseStreamRawSpec) raw; if (!streamNames.Contains(rawStreamSpec.StreamName)) { throw new ExprValidationException( "Property rename '" + rawStreamSpec.StreamName + "' not found in path"); } var streamSpec = new SelectClauseStreamCompiledSpec( rawStreamSpec.StreamName, rawStreamSpec.OptionalAsName); var streamNumber = streamNameAndNumber.Get(rawStreamSpec.StreamName); streamSpec.StreamNumber = streamNumber; cumulativeSelectClause.Add(streamSpec); } else if (raw is SelectClauseExprRawSpec) { var exprSpec = (SelectClauseExprRawSpec) raw; var exprCompiled = ExprNodeUtilityValidate.GetValidatedSubtree( ExprNodeOrigin.CONTAINEDEVENT, exprSpec.SelectExpression, validationContext); var resultName = exprSpec.OptionalAsName; if (resultName == null) { resultName = ExprNodeUtilityPrint.ToExpressionStringMinPrecedenceSafe(exprCompiled); } cumulativeSelectClause.Add( new SelectClauseExprCompiledSpec( exprCompiled, resultName, exprSpec.OptionalAsName, exprSpec.IsEvents)); var isMinimal = ExprNodeUtilityValidate.IsMinimalExpression(exprCompiled); if (isMinimal != null) { throw new ExprValidationException( "Expression in a property-selection may not utilize " + isMinimal); } } else if (raw is SelectClauseElementWildcard) { // wildcards are stream selects: we assign a stream name (any) and add a stream wildcard select var streamNameAtom = atom.OptionalAsName; if (streamNameAtom == null) { streamNameAtom = UuidGenerator.Generate(); } var streamSpec = new SelectClauseStreamCompiledSpec(streamNameAtom, atom.OptionalAsName); var streamNumber = i + 1; streamSpec.StreamNumber = streamNumber; cumulativeSelectClause.Add(streamSpec); } else { throw new IllegalStateException("Unknown select clause item:" + raw); } } } currentEventType = fragmentEventType.FragmentType; fragmentEventTypes[i] = fragmentEventType; containedEventForges[i] = containedEventEval; } if (cumulativeSelectClause.IsEmpty()) { if (length == 1) { return new PropertyEvaluatorSimpleForge( containedEventForges[0], fragmentEventTypes[0], whereClauses[0], expressionTexts[0]); } return new PropertyEvaluatorNestedForge( containedEventForges, fragmentEventTypes, whereClauses, expressionTexts.ToArray()); } { var fragmentEventTypeIsIndexed = new bool[fragmentEventTypes.Length]; for (var i = 0; i < fragmentEventTypes.Length; i++) { fragmentEventTypeIsIndexed[i] = fragmentEventTypes[i].IsIndexed; } var accumulative = new PropertyEvaluatorAccumulativeForge( containedEventForges, fragmentEventTypeIsIndexed, whereClauses, expressionTexts); var whereTypes = streamEventTypes.ToArray(); var whereStreamNames = streamNames.ToArray(); var isIStreamOnly = new bool[streamNames.Count]; isIStreamOnly.Fill(true); StreamTypeService streamTypeService = new StreamTypeServiceImpl( whereTypes, whereStreamNames, isIStreamOnly, false, false); var cumulativeSelectArr = cumulativeSelectClause.ToArray(); var args = new SelectProcessorArgs( cumulativeSelectArr, null, false, null, null, streamTypeService, null, false, rawInfo.Annotations, rawInfo, services); var selectExprDesc = SelectExprProcessorFactory.GetProcessor(args, null, false); return new PropertyEvaluatorSelectForge(selectExprDesc, accumulative); } }