public void Should_be_able_to_create_projections()
		{
			var serviceMock = new Mock<IProjectionService>();
			var processor = new EventProcessor(new EventProcessorConfiguration
			{
				ProjectionService = serviceMock.Object,
			});
			var eventProjection = new EventProjection("Test");
			var handler = new FakeEventHandler();

			serviceMock.Setup(m => m.GetEvent(0)).Returns(FakeEvent(new FakeEvent1 {PropertyOne = "value0"}, 0));
			serviceMock.Setup(m => m.GetEvent(1)).Returns(FakeEvent(new FakeEvent1 {PropertyOne = "value1"}, 1));
			serviceMock.Setup(m => m.GetEvent(2)).Returns(FakeEvent(new FakeEvent2 {PropertyTwo = "value2"}, 2));
			serviceMock.Setup(m => m.GetEvent(3)).Returns(FakeEvent(new FakeEvent1 {PropertyOne = "value3"}, 3));
			serviceMock.Setup(m => m.GetEvent(4)).Returns(FakeEvent(new FakeEvent2 {PropertyTwo = "value4"}, 4));
			serviceMock.Setup(m => m.GetEvent(5)).Returns(FakeEvent(new FakeEvent1 {PropertyOne = "[done]"}, 5));
			serviceMock.Setup(m => m.GetEvent(6)).Returns((ProjectionEvent) null);

			var position = new Queue<int>();

			for (var i = 0; i < 6; i++)
			{
				position.Enqueue(i);
			}

			serviceMock.Setup(m => m.GetSequenceNumber("Test")).Returns(() => position.Count > 0 ? position.Dequeue() : 6);

			eventProjection.AddEventHandler(handler);

			processor.AddEventProjection(eventProjection);

			processor.Start();

			while (!handler.HasValue("[done]"))
			{
				Thread.Sleep(250);
			}

			processor.Stop();
		}
        public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieval tableData)
        {
            // We dynamically adjust the column headers
            // This is the max number of fields we can expect for this table
            int maxFieldCount = Math.Min(AbsoluteMaxFields, tableData.QueryOutput <int>(
                                             new DataOutputPath(PerfettoPluginConstants.FtraceEventCookerPath, nameof(PerfettoFtraceEventCooker.MaximumEventFieldCount))));

            // Get data from the cooker
            var events = tableData.QueryOutput <ProcessedEventData <PerfettoFtraceEvent> >(
                new DataOutputPath(PerfettoPluginConstants.FtraceEventCookerPath, nameof(PerfettoFtraceEventCooker.FtraceEvents)));

            var tableGenerator  = tableBuilder.SetRowCount((int)events.Count);
            var eventProjection = new EventProjection <PerfettoFtraceEvent>(events);

            var processNameColumn = new DataColumn <string>(
                ProcessNameColumn,
                eventProjection.Compose((ftraceEvent) => ftraceEvent.ProcessFormattedName));

            tableGenerator.AddColumn(processNameColumn);

            var threadNameColumn = new DataColumn <string>(
                ThreadNameColumn,
                eventProjection.Compose((ftraceEvent) => ftraceEvent.ThreadFormattedName));

            tableGenerator.AddColumn(threadNameColumn);

            var startTimestampColumn = new DataColumn <Timestamp>(
                StartTimestampColumn,
                eventProjection.Compose((ftraceEvent) => ftraceEvent.StartTimestamp));

            tableGenerator.AddColumn(startTimestampColumn);

            var cpuColumn = new DataColumn <uint>(
                CpuColumn,
                eventProjection.Compose((ftraceEvent) => ftraceEvent.Cpu));

            tableGenerator.AddColumn(cpuColumn);

            var nameColumn = new DataColumn <string>(
                NameColumn,
                eventProjection.Compose((ftraceEvent) => ftraceEvent.Name));

            tableGenerator.AddColumn(nameColumn);

            List <ColumnConfiguration> fieldColumns = new List <ColumnConfiguration>();

            // Add the field columns, with column names depending on the given event
            for (int index = 0; index < maxFieldCount; index++)
            {
                var    colIndex  = index; // This seems unncessary but causes odd runtime behavior if not done this way. Compiler is confused perhaps because w/o this func will index=event.FieldNames.Count every time. index is passed as ref but colIndex as value into func
                string fieldName = "Field " + (colIndex + 1);

                var eventFieldNameProjection = eventProjection.Compose((ftraceEvent) => colIndex < ftraceEvent.Args?.Count ? ftraceEvent.Args.ElementAt(colIndex).Key : string.Empty);

                // generate a column configuration
                var fieldColumnConfiguration = new ColumnConfiguration(
                    new ColumnMetadata(Common.GenerateGuidFromName(fieldName), fieldName, eventFieldNameProjection, fieldName),
                    new UIHints
                {
                    IsVisible     = true,
                    Width         = 150,
                    TextAlignment = TextAlignment.Left,
                });

                // Add this column to the column order
                fieldColumns.Add(fieldColumnConfiguration);

                var eventFieldAsStringProjection = eventProjection.Compose((ftraceEvent) => colIndex < ftraceEvent.Args?.Count ? ftraceEvent.Args.ElementAt(colIndex).Value : string.Empty);

                tableGenerator.AddColumn(fieldColumnConfiguration, eventFieldAsStringProjection);
            }

            // Start construction of the column order. Pivot on CPU
            List <ColumnConfiguration> cpuColumns = new List <ColumnConfiguration>()
            {
                CpuColumn,
                TableConfiguration.PivotColumn, // Columns before this get pivotted
                ProcessNameColumn,
                ThreadNameColumn,
                NameColumn,
            };

            cpuColumns.AddRange(fieldColumns);
            cpuColumns.Add(TableConfiguration.GraphColumn); // Columns after this get graphed
            cpuColumns.Add(StartTimestampColumn);

            var cpuConfig = new TableConfiguration("CPU")
            {
                Columns = cpuColumns,
            };

            cpuConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);

            // Start construction of the column order. Pivot on process and thread
            List <ColumnConfiguration> processThreadColumns = new List <ColumnConfiguration>()
            {
                ProcessNameColumn,
                ThreadNameColumn,
                NameColumn,
                TableConfiguration.PivotColumn, // Columns before this get pivotted
                CpuColumn,
            };

            processThreadColumns.AddRange(fieldColumns);
            processThreadColumns.Add(TableConfiguration.GraphColumn); // Columns after this get graphed
            processThreadColumns.Add(StartTimestampColumn);

            var processThreadConfig = new TableConfiguration("Process-Thread")
            {
                Columns = processThreadColumns,
            };

            processThreadConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);

            tableBuilder
            .AddTableConfiguration(cpuConfig)
            .AddTableConfiguration(processThreadConfig)
            .SetDefaultTableConfiguration(processThreadConfig);
        }
        public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieval tableData)
        {
            int maximumFieldCount = tableData.QueryOutput <int>(
                DataOutputPath.ForSource(LTTngConstants.SourceId, LTTngGenericEventDataCooker.Identifier, nameof(LTTngGenericEventDataCooker.MaximumEventFieldCount)));

            var events = tableData.QueryOutput <ProcessedEventData <LTTngGenericEvent> >(
                DataOutputPath.ForSource(LTTngConstants.SourceId, LTTngGenericEventDataCooker.Identifier, nameof(LTTngGenericEventDataCooker.Events)));

            var tableGenerator = tableBuilder.SetRowCount((int)events.Count);

            var genericEventProjection = new EventProjection <LTTngGenericEvent>(events);

            var eventNameColumn = new DataColumn <string>(
                eventNameColumnConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.EventName));

            tableGenerator.AddColumn(eventNameColumn);

            var eventIdColumn = new DataColumn <uint>(
                eventIdColumnConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Id));

            tableGenerator.AddColumn(eventIdColumn);

            var cpuIdColumn = new DataColumn <uint>(
                cpuIdColumnConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.CpuId));

            tableGenerator.AddColumn(cpuIdColumn);

            var discardedEventsColumn = new DataColumn <uint>(
                discardedEventsColumnConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.DiscardedEvents));

            tableGenerator.AddColumn(discardedEventsColumn);

            var eventTimestampColumn = new DataColumn <Timestamp>(
                eventTimestampColumnConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Timestamp));

            tableGenerator.AddColumn(eventTimestampColumn);

            tableGenerator.AddColumn(countColumnConfig, Projection.Constant(1));

            // Add the field columns, with column names depending on the given event
            for (int index = 0; index < maximumFieldCount; index++)
            {
                var    colIndex  = index; // This seems unncessary but causes odd runtime behavior if not done this way. Compiler is confused perhaps because w/o this func will index=genericEvent.FieldNames.Count every time. index is passed as ref but colIndex as value into func
                string fieldName = "Field " + (colIndex + 1);

                var genericEventFieldNameProjection = genericEventProjection.Compose((genericEvent) => colIndex < genericEvent.FieldNames.Count ? genericEvent.FieldNames[colIndex] : string.Empty);

                // generate a column configuration
                var fieldColumnConfiguration = new ColumnConfiguration(
                    new ColumnMetadata(Common.GenerateGuidFromName(fieldName), fieldName, genericEventFieldNameProjection, fieldName),
                    new UIHints
                {
                    IsVisible     = true,
                    Width         = 80,
                    TextAlignment = TextAlignment.Left,
                });

                var genericEventFieldAsStringProjection = genericEventProjection.Compose((genericEvent) => colIndex < genericEvent.FieldNames.Count ? genericEvent.FieldValues[colIndex] : string.Empty);

                tableGenerator.AddColumn(fieldColumnConfiguration, genericEventFieldAsStringProjection);
            }
        }
        public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieval tableData)
        {
            // We dynamically adjust the column headers
            // This is the max number of fields we can expect for this table
            int maxFieldCount = Math.Min(AbsoluteMaxFields, tableData.QueryOutput <int>(
                                             new DataOutputPath(PerfettoPluginConstants.GenericEventCookerPath, nameof(PerfettoGenericEventCooker.MaximumEventFieldCount))));

            bool hasProviders = tableData.QueryOutput <bool>(
                new DataOutputPath(PerfettoPluginConstants.GenericEventCookerPath, nameof(PerfettoGenericEventCooker.HasProviders)));

            // Get data from the cooker
            var events = tableData.QueryOutput <ProcessedEventData <PerfettoGenericEvent> >(
                new DataOutputPath(PerfettoPluginConstants.GenericEventCookerPath, nameof(PerfettoGenericEventCooker.GenericEvents)));

            var tableGenerator         = tableBuilder.SetRowCount((int)events.Count);
            var genericEventProjection = new EventProjection <PerfettoGenericEvent>(events);

            // Add all the data projections
            var sliceIdColumn = new DataColumn <int>(
                SliceIdColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.SliceId));

            tableGenerator.AddColumn(sliceIdColumn);

            var processNameColumn = new DataColumn <string>(
                ProcessNameColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Process));

            tableGenerator.AddColumn(processNameColumn);

            var processLabelColumn = new DataColumn <string>(
                ProcessLabelColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.ProcessLabel));

            tableGenerator.AddColumn(processLabelColumn);
            if (events.Any(f => !String.IsNullOrWhiteSpace(f.ProcessLabel)))
            {
                ProcessLabelColConfig.DisplayHints.IsVisible = true;
            }

            var threadNameColumn = new DataColumn <string>(
                ThreadNameColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Thread));

            tableGenerator.AddColumn(threadNameColumn);

            var eventNameColumn = new DataColumn <string>(
                EventNameColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.EventName));

            tableGenerator.AddColumn(eventNameColumn);

            var startTimestampColumn = new DataColumn <Timestamp>(
                StartTimestampColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.StartTimestamp));

            tableGenerator.AddColumn(startTimestampColumn);

            var endTimestampColumn = new DataColumn <Timestamp>(
                EndTimestampColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.EndTimestamp));

            tableGenerator.AddColumn(endTimestampColumn);

            var durationColumn = new DataColumn <TimestampDelta>(
                DurationNotSortedColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Duration));

            tableGenerator.AddColumn(durationColumn);

            var durationExColumn = new DataColumn <TimestampDelta>(
                DurationExSortedColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.DurationExclusive));

            tableGenerator.AddColumn(durationExColumn);

            var categoryColumn = new DataColumn <string>(
                CategoryColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Category));

            tableGenerator.AddColumn(categoryColumn);

            var typeColumn = new DataColumn <string>(
                TypeColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Type));

            tableGenerator.AddColumn(typeColumn);

            var providerColumn = new DataColumn <string>(
                ProviderColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Provider));

            tableGenerator.AddColumn(providerColumn);

            var trackNameIdColumn = new DataColumn <string>(
                TrackNameIdColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.ThreadTrack != null ?
                                               (!String.IsNullOrWhiteSpace(genericEvent.ThreadTrack.Name) ? $"{genericEvent.ThreadTrack.Name} ({genericEvent.ThreadTrack.Id})" : genericEvent.ThreadTrack.Id.ToString())
                                                                    : String.Empty));

            tableGenerator.AddColumn(trackNameIdColumn);

            var parentIdColumn = new DataColumn <int>(
                ParentIdColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.ParentId.HasValue ? genericEvent.ParentId.Value : -1));

            tableGenerator.AddColumn(parentIdColumn);

            var parentDepthLevelColumn = new DataColumn <int>(
                ParentDepthLevelColConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.ParentTreeDepthLevel));

            tableGenerator.AddColumn(parentDepthLevelColumn);

            tableGenerator.AddHierarchicalColumn(ParentEventNameTreeBranchColConfig, genericEventProjection.Compose((genericEvent) => genericEvent.ParentEventNameTree), new ArrayAccessProvider <string>());

            tableGenerator.AddColumn(CountColConfig, Projection.Constant <int>(1));

            // The provider column is optionally populated depending on whether or not the user specified a ProviderGUID mapping file
            ProviderColConfig.DisplayHints.IsVisible = hasProviders;

            List <ColumnConfiguration> fieldColumns = new List <ColumnConfiguration>();

            // Add the field columns, with column names depending on the given event
            for (int index = 0; index < maxFieldCount; index++)
            {
                var    colIndex  = index; // This seems unncessary but causes odd runtime behavior if not done this way. Compiler is confused perhaps because w/o this func will index=genericEvent.FieldNames.Count every time. index is passed as ref but colIndex as value into func
                string fieldName = "Field " + (colIndex + 1);

                var genericEventFieldNameProjection = genericEventProjection.Compose((genericEvent) => colIndex < genericEvent.Args?.Count ? genericEvent.Args.ElementAt(colIndex).Key : string.Empty);

                // generate a column configuration
                var fieldColumnConfiguration = new ColumnConfiguration(
                    new ColumnMetadata(Common.GenerateGuidFromName(fieldName), fieldName, genericEventFieldNameProjection, fieldName)
                {
                    IsDynamic = true
                },
                    new UIHints
                {
                    IsVisible     = true,
                    Width         = 150,
                    TextAlignment = TextAlignment.Left,
                });

                // Add this column to the column order
                fieldColumns.Add(fieldColumnConfiguration);

                var genericEventFieldAsStringProjection = genericEventProjection.Compose((genericEvent) => colIndex < genericEvent.Args?.Count ? genericEvent.Args.ElementAt(colIndex).Value : string.Empty);

                tableGenerator.AddColumn(fieldColumnConfiguration, genericEventFieldAsStringProjection);
            }

            List <ColumnConfiguration> defaultColumns = new List <ColumnConfiguration>()
            {
                ProviderColConfig,
                ProcessNameColConfig,
                ProcessLabelColConfig,
                ThreadNameColConfig,
                TableConfiguration.PivotColumn, // Columns before this get pivotted on
                EventNameColConfig,
                CategoryColConfig,
                TypeColConfig,
                DurationNotSortedColConfig,
                DurationExSortedColConfig,
            };

            defaultColumns.AddRange(fieldColumns);
            defaultColumns.Add(TableConfiguration.GraphColumn); // Columns after this get graphed
            defaultColumns.Add(StartTimestampColConfig);
            defaultColumns.Add(EndTimestampColConfig);

            // Proces-Thread config
            var processThreadConfig = new TableConfiguration("Process-Thread")
            {
                Columns = defaultColumns,
            };

            SetGraphTableConfig(processThreadConfig);
            tableBuilder.AddTableConfiguration(processThreadConfig);

            // Process-Thread by StartTime config
            var processThreadByStartTimeColumns = new List <ColumnConfiguration>(defaultColumns);

            processThreadByStartTimeColumns.Remove(EndTimestampColConfig);
            processThreadByStartTimeColumns.Insert(processThreadByStartTimeColumns.Count - 2, EndTimestampColConfig);

            var processThreadByStartTimeConfig = new TableConfiguration("Process-Thread by Start Time")
            {
                Columns = processThreadByStartTimeColumns,
            };

            SetGraphTableConfig(processThreadByStartTimeConfig);
            tableBuilder.AddTableConfiguration(processThreadByStartTimeConfig);

            // Process-Thread Activity config
            var processThreadActivityColumns = new List <ColumnConfiguration>(defaultColumns);

            processThreadActivityColumns.Remove(StartTimestampColConfig);
            processThreadActivityColumns.Insert(10, StartTimestampColConfig);
            processThreadActivityColumns.Remove(EndTimestampColConfig);
            processThreadActivityColumns.Insert(11, EndTimestampColConfig);
            processThreadActivityColumns.Add(CountSortedColConfig);

            // Different sorting than default
            processThreadActivityColumns.Remove(DurationExSortedColConfig);
            processThreadActivityColumns.Insert(9, DurationExNotSortedMaxColConfig);
            DurationExNotSortedMaxColConfig.DisplayHints.SortPriority = 1;
            DurationExNotSortedMaxColConfig.DisplayHints.SortOrder    = SortOrder.Descending;

            var processThreadActivityConfig = new TableConfiguration("Process-Thread Activity")
            {
                Columns = processThreadActivityColumns,
            };

            SetGraphTableConfig(processThreadActivityConfig);
            tableBuilder.AddTableConfiguration(processThreadActivityConfig);

            // Default - Process-Thread-NestedSlices-Name config
            var processThreadNameColumns = new List <ColumnConfiguration>(defaultColumns);

            processThreadNameColumns.Insert(4, ParentDepthLevelColConfig);
            processThreadNameColumns.Remove(EventNameColConfig);
            processThreadNameColumns.Insert(5, EventNameColConfig);
            var processThreadNestedNameConfig = new TableConfiguration("Process-Thread-NestedSlices-Name")
            {
                Columns = processThreadNameColumns,
            };

            SetGraphTableConfig(processThreadNestedNameConfig);
            tableBuilder.AddTableConfiguration(processThreadNestedNameConfig)
            .SetDefaultTableConfiguration(processThreadNestedNameConfig);

            // Process-Thread-EventName config
            var processThreadEventNameColumns = new List <ColumnConfiguration>(defaultColumns);

            processThreadEventNameColumns.Remove(EventNameColConfig);
            processThreadEventNameColumns.Insert(4, EventNameColConfig);
            var processThreadEventNameConfig = new TableConfiguration("Process-Thread-Name")
            {
                Columns = processThreadEventNameColumns,
            };

            SetGraphTableConfig(processThreadEventNameConfig);
            tableBuilder.AddTableConfiguration(processThreadEventNameConfig);

            // Process-Thread-Name by StartTime config
            var processThreadNameByStartTimeColumns = new List <ColumnConfiguration>(defaultColumns);

            processThreadNameByStartTimeColumns.Insert(4, ParentDepthLevelColConfig);
            processThreadNameByStartTimeColumns.Remove(EventNameColConfig);
            processThreadNameByStartTimeColumns.Insert(5, EventNameColConfig);
            processThreadNameByStartTimeColumns.Remove(EndTimestampColConfig);
            processThreadNameByStartTimeColumns.Insert(processThreadNameByStartTimeColumns.Count - 2, EndTimestampColConfig);

            var processThreadNameByStartTimeConfig = new TableConfiguration("Process-Thread-Name by Start Time")
            {
                Columns = processThreadNameByStartTimeColumns,
            };

            SetGraphTableConfig(processThreadNameByStartTimeConfig);
            tableBuilder.AddTableConfiguration(processThreadNameByStartTimeConfig);

            // Process-Thread-ParentTree config
            var processThreadNameTreeColumns = new List <ColumnConfiguration>(defaultColumns);

            processThreadNameTreeColumns.Insert(4, ParentEventNameTreeBranchColConfig);
            processThreadNameTreeColumns.Remove(DurationExSortedColConfig);
            processThreadNameTreeColumns.Insert(10, DurationExSortedSumColConfig);
            var processThreadParentNameTreeConfig = new TableConfiguration("Process-Thread-ParentTree")
            {
                Columns = processThreadNameTreeColumns,
            };

            ParentEventNameTreeBranchColConfig.DisplayHints.IsVisible = true;
            SetGraphTableConfig(processThreadParentNameTreeConfig);
            tableBuilder.AddTableConfiguration(processThreadParentNameTreeConfig);
        }
 public void SetUp()
 {
     _convertedEventArgs = new object();
     _conversionFunc     = eventArgs => _convertedEventArgs;
     _eventProjection    = new EventProjection <object, object>(_conversionFunc);
 }
        public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieval tableData)
        {
            int maximumFieldCount = tableData.QueryOutput <int>(
                DataOutputPath.ForSource(PerfConstants.SourceId, PerfGenericEventDataCooker.Identifier, nameof(PerfGenericEventDataCooker.MaximumEventFieldCount)));

            var events = tableData.QueryOutput <ProcessedEventData <PerfGenericEvent> >(
                DataOutputPath.ForSource(PerfConstants.SourceId, PerfGenericEventDataCooker.Identifier, nameof(PerfGenericEventDataCooker.Events)));

            var tableGenerator = tableBuilder.SetRowCount((int)events.Count);

            var genericEventProjection = new EventProjection <PerfGenericEvent>(events);

            var eventNameColumn = new DataColumn <string>(
                eventNameColumnConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.EventName));

            tableGenerator.AddColumn(eventNameColumn);

            var eventIdColumn = new DataColumn <uint>(
                eventIdColumnConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Id));

            tableGenerator.AddColumn(eventIdColumn);

            var cpuIdColumn = new DataColumn <uint>(
                cpuIdColumnConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.CpuId));

            tableGenerator.AddColumn(cpuIdColumn);

            var eventTimestampColumn = new DataColumn <Timestamp>(
                eventTimestampColumnConfig,
                genericEventProjection.Compose((genericEvent) => genericEvent.Timestamp));

            tableGenerator.AddColumn(eventTimestampColumn);

            tableGenerator.AddColumn(countColumnConfig, Projection.Constant(1));

            // Add the field columns, with column names depending on the given event
            for (int columnIndex = 0; columnIndex < maximumFieldCount; columnIndex++)
            {
                string fieldName = "Field " + (columnIndex + 1);

                var genericEventFieldProjection = new GenericEventFieldProjection(columnIndex, genericEventProjection);

                var genericEventFieldNameProjection =
                    genericEventFieldProjection.Compose((field) => field.HasValue ? field.Value.Name : string.Empty);

                // generate a column configuration
                var fieldColumnConfiguration = new ColumnConfiguration(
                    new ColumnMetadata(Common.GenerateGuidFromName(fieldName), fieldName, genericEventFieldNameProjection, fieldName),
                    new UIHints
                {
                    IsVisible     = true,
                    Width         = 80,
                    TextAlignment = TextAlignment.Left,
                });

                var genericEventFieldAsStringProjection =
                    genericEventFieldProjection.Compose((field) => field.HasValue ? field.Value.Value : string.Empty);

                tableGenerator.AddColumn(fieldColumnConfiguration, genericEventFieldAsStringProjection);
            }
        }