context: ///////////////////////////////////////////////////////////////////////////////////////
  // Copyright (C) 2006-2019 Esper Team. All rights reserved.                           /
  // http://esper.codehaus.org                                                          /
  // ---------------------------------------------------------------------------------- /
  // The software in this package is published under the terms of the GPL license       /
  // a copy of which has been included with this distribution in the license.txt file.  /
  ///////////////////////////////////////////////////////////////////////////////////////
  
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Reflection;
  
  using com.espertech.esper.common.client;
  using com.espertech.esper.common.client.meta;
  using com.espertech.esper.common.client.util;
  using com.espertech.esper.common.@internal.bytecodemodel.@base;
  using com.espertech.esper.common.@internal.bytecodemodel.model.expression;
  using com.espertech.esper.common.@internal.compile.stage1.spec;
  using com.espertech.esper.common.@internal.compile.stage2;
  using com.espertech.esper.common.@internal.compile.stage3;
  using com.espertech.esper.common.@internal.epl.expression.core;
  using com.espertech.esper.common.@internal.epl.expression.etc;
  using com.espertech.esper.common.@internal.epl.namedwindow.path;
  using com.espertech.esper.common.@internal.epl.resultset.core;
  using com.espertech.esper.common.@internal.epl.resultset.select.eval;
  using com.espertech.esper.common.@internal.epl.resultset.select.typable;
  using com.espertech.esper.common.@internal.epl.table.compiletime;
  using com.espertech.esper.common.@internal.@event.arr;
  using com.espertech.esper.common.@internal.@event.avro;
  using com.espertech.esper.common.@internal.@event.bean.core;
  using com.espertech.esper.common.@internal.@event.bean.service;
  using com.espertech.esper.common.@internal.@event.core;
  using com.espertech.esper.common.@internal.@event.json.compiletime;
  using com.espertech.esper.common.@internal.@event.json.core;
  using com.espertech.esper.common.@internal.@event.map;
  using com.espertech.esper.common.@internal.@event.variant;
  using com.espertech.esper.common.@internal.rettype;
  using com.espertech.esper.common.@internal.settings;
  using com.espertech.esper.common.@internal.util;
  using com.espertech.esper.compat;
  using com.espertech.esper.compat.collections;
  using com.espertech.esper.compat.logging;
  using com.espertech.esper.container;
  
  using static com.espertech.esper.common.@internal.bytecodemodel.model.expression.CodegenExpressionBuilder;
  
  namespace com.espertech.esper.common.@internal.epl.resultset.select.core
  {
      /// <summary>
      ///     Processor for select-clause expressions that handles a list of selection items represented by
      ///     expression nodes. Computes results based on matching events.
      /// </summary>
      public class SelectExprProcessorHelper
      {
          private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  
          private readonly IContainer _container;
          private readonly SelectProcessorArgs _args;
          private readonly InsertIntoDesc _insertIntoDesc;
          private readonly IList<SelectExprStreamDesc> _selectedStreams;
          private readonly IList<SelectClauseExprCompiledSpec> _selectionList;
  
          public SelectExprProcessorHelper(
              IList<SelectClauseExprCompiledSpec> selectionList,
              IList<SelectExprStreamDesc> selectedStreams,
              SelectProcessorArgs args,
              InsertIntoDesc insertIntoDesc)
          {
              _container = args.Container;
              _selectionList = selectionList;
              _selectedStreams = selectedStreams;
              _args = args;
              _insertIntoDesc = insertIntoDesc;
          }
  
          public SelectExprProcessorWInsertTarget Forge {
              get {
                  var isUsingWildcard = _args.IsUsingWildcard;
                  var typeService = _args.TypeService;
                  var importService = _args.ImportService;
                  BeanEventTypeFactory beanEventTypeFactoryProtected = _args.BeanEventTypeFactoryPrivate;
                  var eventTypeNameGeneratorStatement =
                      _args.CompileTimeServices.EventTypeNameGeneratorStatement;
                  var moduleName = _args.ModuleName;
                  var additionalForgeables = new List<StmtClassForgeableFactory>();
  
                  // Get the named and un-named stream selectors (i.e. select s0.* from S0 as s0), if any
                  IList<SelectClauseStreamCompiledSpec> namedStreams = new List<SelectClauseStreamCompiledSpec>();
                  IList<SelectExprStreamDesc> unnamedStreams = new List<SelectExprStreamDesc>();
                  foreach (var spec in _selectedStreams) {
                      // handle special "transpose(...)" function
                      if (spec.StreamSelected != null &&
                          spec.StreamSelected.OptionalName == null ||
                          spec.ExpressionSelectedAsStream != null) {
                          unnamedStreams.Add(spec);
                      }
                      else {
                          namedStreams.Add(spec.StreamSelected);
                          if (spec.StreamSelected.IsProperty) {
                              throw new ExprValidationException(
                                  "The property wildcard syntax must be used without column name");
                          }
                      }
                  }
  
                  // Error if there are more then one un-named streams (i.e. select s0.*, s1.* from S0 as s0, S1 as s1)
                  // Thus there is only 1 unnamed stream selector maximum.
                  if (unnamedStreams.Count > 1) {
                      throw new ExprValidationException(
                          "A column name must be supplied for all but one stream if multiple streams are selected via the stream.* notation");
                  }
  
                  if (_selectedStreams.IsEmpty() && _selectionList.IsEmpty() && !isUsingWildcard) {
                      throw new ArgumentException("Empty selection list not supported");
                  }
  
                  foreach (var entry in _selectionList) {
                      if (entry.AssignedName == null) {
                          throw new ArgumentException("Expected name for each expression has not been supplied");
                      }
                  }
  
                  // Verify insert into clause
                  if (_insertIntoDesc != null) {
                      VerifyInsertInto(_insertIntoDesc, _selectionList);
                  }
  
                  // Build a subordinate wildcard processor for joins
                  SelectExprProcessorForge joinWildcardProcessor = null;
                  if (typeService.StreamNames.Length > 1 && isUsingWildcard) {
                      var pair = SelectExprJoinWildcardProcessorFactory.Create(
                          _args, null, eventTypeName => eventTypeName + "_join");
                      joinWildcardProcessor = pair.Forge;
                      additionalForgeables.AddAll(pair.AdditionalForgeables);
                  }
  
                  // Resolve underlying event type in the case of wildcard select
                  EventType eventType = null;
                  var singleStreamWrapper = false;
                  if (isUsingWildcard) {
                      if (joinWildcardProcessor != null) {
                          eventType = joinWildcardProcessor.ResultEventType;
                      }
                      else {
                          eventType = typeService.EventTypes[0];
                          if (eventType is WrapperEventType) {
                              singleStreamWrapper = true;
                          }
                      }
                  }
  
                  // Find if there is any fragments selected
                  EventType insertIntoTargetType = null;
                  if (_insertIntoDesc != null) {
                      if (_args.OptionalInsertIntoEventType != null) {
                          insertIntoTargetType = _args.OptionalInsertIntoEventType;
                      }
                      else {
                          insertIntoTargetType =
                              _args.EventTypeCompileTimeResolver.GetTypeByName(_insertIntoDesc.EventTypeName);
                          if (insertIntoTargetType == null) {
                              var table = _args.TableCompileTimeResolver.Resolve(_insertIntoDesc.EventTypeName);
                              if (table != null) {
                                  insertIntoTargetType = table.InternalEventType;
                                  _args.OptionalInsertIntoEventType = insertIntoTargetType;
                              }
                          }
                      }
                  }
  
                  // Obtain insert-into per-column type information, when available
                  var insertInfo = DetermineInsertedEventTypeTargets(insertIntoTargetType, _selectionList, _insertIntoDesc);
                  var insertIntoTargetsPerCol = insertInfo.InsertIntoTargetsPerCol;
                  var insertIntoPropertyDescriptors = insertInfo.PropertyDescriptors;
  
                  // Get expression nodes
                  var exprForges = new ExprForge[_selectionList.Count];
                  var exprNodes = new ExprNode[_selectionList.Count];
                  var expressionReturnTypes = new object[_selectionList.Count];
                  for (var i = 0; i < _selectionList.Count; i++) {
                      var spec = _selectionList[i];
                      var expr = spec.SelectExpression;
                      var forge = expr.Forge;
                      exprNodes[i] = expr;
  
                      // if there is insert-into specification, use that
                      if (_insertIntoDesc != null) {
                          // handle insert-into, with well-defined target event-typed column, and enumeration
                          var pairInner = HandleInsertIntoEnumeration(
                              spec.ProvidedName,
                              insertIntoTargetsPerCol[i],
                              expr,
                              forge);
                          if (pairInner != null) {
                              expressionReturnTypes[i] = pairInner.Type;
                              exprForges[i] = pairInner.Forge;
                              continue;
                          }
  
                          // handle insert-into with well-defined target event-typed column, and typable expression
                          pairInner = HandleInsertIntoTypableExpression(insertIntoTargetsPerCol[i], forge, _args);
                          if (pairInner != null) {
                              expressionReturnTypes[i] = pairInner.Type;
                              exprForges[i] = pairInner.Forge;
                              continue;
                          }
                      }
  
                      // handle @eventbean annotation, i.e. well-defined type through enumeration
                      var pair = HandleAtEventbeanEnumeration(spec.IsEvents, forge);
                      if (pair != null) {
                          expressionReturnTypes[i] = pair.Type;
                          exprForges[i] = pair.Forge;
                          continue;
                      }
  
                      // handle typeable return, i.e. typable multi-column return without provided target type
                      pair = HandleTypableExpression(forge, i, eventTypeNameGeneratorStatement);
                      if (pair != null) {
                          expressionReturnTypes[i] = pair.Type;
                          exprForges[i] = pair.Forge;
                          continue;
                      }
  
                      // handle select-clause expressions that match group-by expressions with rollup and therefore should be boxed types as rollup can produce a null value
                      if (_args.GroupByRollupInfo != null && _args.GroupByRollupInfo.RollupDesc != null) {
                          var returnType = forge.EvaluationType;
                          var returnTypeBoxed = returnType.GetBoxedType();
                          if (returnType != returnTypeBoxed &&
                              IsGroupByRollupNullableExpression(expr, _args.GroupByRollupInfo)) {
                              exprForges[i] = forge;
                              expressionReturnTypes[i] = returnTypeBoxed;
                              continue;
                          }
                      }
  
                      // assign normal expected return type
                      exprForges[i] = forge;
                      expressionReturnTypes[i] = exprForges[i].EvaluationType;
                  }
  
                  // Get column names
                  string[] columnNames;
                  string[] columnNamesAsProvided;
                  if (_insertIntoDesc != null && !_insertIntoDesc.ColumnNames.IsEmpty()) {
                      columnNames = _insertIntoDesc.ColumnNames.ToArray();
                      columnNamesAsProvided = columnNames;
                  }
                  else if (!_selectedStreams.IsEmpty()) { // handle stream selection column names
                      var numStreamColumnsJoin = 0;
                      if (isUsingWildcard && typeService.EventTypes.Length > 1) {
                          numStreamColumnsJoin = typeService.EventTypes.Length;
                      }
  
                      columnNames = new string[_selectionList.Count + namedStreams.Count + numStreamColumnsJoin];
                      columnNamesAsProvided = new string[columnNames.Length];
                      var countInner = 0;
                      foreach (var aSelectionList in _selectionList) {
                          columnNames[countInner] = aSelectionList.AssignedName;
                          columnNamesAsProvided[countInner] = aSelectionList.ProvidedName;
                          countInner++;
                      }
  
                      foreach (var aSelectionList in namedStreams) {
                          columnNames[countInner] = aSelectionList.OptionalName;
                          columnNamesAsProvided[countInner] = aSelectionList.OptionalName;
                          countInner++;
                      }
  
                      // for wildcard joins, add the streams themselves
                      if (isUsingWildcard && typeService.EventTypes.Length > 1) {
                          foreach (var streamName in typeService.StreamNames) {
                              columnNames[countInner] = streamName;
                              columnNamesAsProvided[countInner] = streamName;
                              countInner++;
                          }
                      }
                  }
                  else {
                      // handle regular column names
  
gt:                     columnNames = new string[_selectionList.Count];