public bool ProcessSegments() { bool processedSegments = false; bool isRuleEnabled = this.FireRuleProcessing( RulesEngineUtils.SEGMENTS_RULE ); if( isRuleEnabled ) { BooleanWrapper processed = new BooleanWrapper( false ); Dictionary<string, object> contextObjects = new Dictionary<string, object>(); contextObjects.Add( "InstanceReport", this ); contextObjects.Add( "Processed", processed ); builderRules.ProcessRule( RulesEngineUtils.SEGMENTS_RULE, contextObjects ); processedSegments = processed.Value; foreach( InstanceReportRow irr in this.Rows ) { if( irr.IsSegmentTitle ) { irr.Label = Regex.Replace( irr.Label, @"\{.*?\} : ", String.Empty ).Trim(); } } this.FireRuleProcessed( RulesEngineUtils.SEGMENTS_RULE ); } return processedSegments; }
public bool ProcessSegments_Rule( BooleanWrapper processed ) { //collect all of the segments to be permuted vertically List<InstanceReportColumn> uniqueSegmentColumns = this.GetSegmentScenarioColumnsForSegmentProcessing(); //if( uniqueSegmentColumns.Count < 2 ) // return false; //collect all of the calendars to be retained horizontally Dictionary<int, List<InstanceReportColumn>> calendarsBySegment = this.GetSegmentScenarioCalendars( uniqueSegmentColumns ); //if( uniqueSegmentColumns.Count < 2 ) // return false; //find the set of `allCalendars` which will hold all data without any adjustment List<InstanceReportColumn> allCalendars; bool doCalendarsOverlap = DoCalendarsOverlap( calendarsBySegment, out allCalendars ); if( !doCalendarsOverlap ) return false; List<Segment> commonSegmentsToPromote = GetCommonSegments( uniqueSegmentColumns ); PromoteConsolidatedSegment( uniqueSegmentColumns, commonSegmentsToPromote ); //set up a temporary holder for the rows - this helps with debugging InstanceReport segmentCandidate = new InstanceReport(); segmentCandidate.Columns.AddRange( allCalendars ); //The first row in every report is usually `IsReportTitle` - copy it to the temporary report InstanceReportRow reportTitleRow = this.FindCloneAndTruncate( row => row.IsReportTitle, allCalendars.Count ); if( reportTitleRow != null ) { reportTitleRow.Id = 0; segmentCandidate.Rows.Add( reportTitleRow ); } //Now, for every `segmentSet`, rebuild the data vertically foreach( InstanceReportColumn segmentSet in uniqueSegmentColumns ) { //If this segment set has segments, create an `IsSegmentTitle` row if( segmentSet.Segments != null && segmentSet.Segments.Count > 0 ) { string label = string.Empty; foreach( Segment seg in segmentSet.Segments ) { if( commonSegmentsToPromote != null && commonSegmentsToPromote.Count > 0 ) { bool isCommon = ReportUtils.Exists( commonSegmentsToPromote, cSeg => cSeg.Equals( seg ) ); if( isCommon ) continue; } if( !string.IsNullOrEmpty( label ) ) label += " | "; label += seg.ValueName; } if( !string.IsNullOrEmpty( label ) ) { InstanceReportRow segmentRow = new InstanceReportRow( label, allCalendars.Count ); segmentRow.IsSegmentTitle = true; segmentRow.Id = segmentCandidate.Rows.Count; segmentRow.OriginalInstanceReportColumn = (InstanceReportColumn)segmentSet.Clone(); segmentCandidate.Rows.Add( segmentRow ); } } //`segmentSets` combined with `rows` provide our vertical (y) axis //for each row in the "base" report, we need to pull in the data InstanceReportRow lastAbstract = null; foreach( InstanceReportRow row in this.Rows ) { if( row.IsReportTitle ) continue; //retain abstracts... if( row.IsAbstractGroupTitle ) { //...unless they're consecutive - retain the most recent one if( lastAbstract != null ) { int at = segmentCandidate.Rows.IndexOf( lastAbstract ); if( at == segmentCandidate.Rows.Count - 1 ) segmentCandidate.Rows.RemoveAt( at ); } InstanceReportRow abstractRow = CloneAndTruncate( row, allCalendars.Count ); abstractRow.Id = segmentCandidate.Rows.Count; segmentCandidate.Rows.Add( abstractRow ); lastAbstract = abstractRow; continue; } //`calendars` provide our horizontal (x) axis //`calendars` (x) combined with `segmentSets` & `rows` (y) allow us to look up the correct cell bool hasData = false; InstanceReportRow currentRow = new InstanceReportRow(); foreach( InstanceReportColumn calendar in allCalendars ) { List<InstanceReportColumn> matches = this.GetMatchingColumns( calendar, segmentSet, row ); //apply exact match InstanceReportColumn exactColumn = matches.Find( m => m.ReportingPeriodEquals( calendar ) ); if( exactColumn != null ) { Cell exactCell = row.Cells.Find( c => c.Id == exactColumn.Id ); if( exactCell != null && exactCell.HasData ) { hasData = true; Cell newCell = (Cell)exactCell.Clone(); newCell.EmbeddedReport = exactCell.EmbeddedReport; if( !string.IsNullOrEmpty( segmentSet.CurrencyCode ) ) { if( (int)row.Unit == (int)UnitType.Monetary || (int)row.Unit == (int)UnitType.EPS ) { newCell.CurrencyCode = segmentSet.CurrencyCode; newCell.CurrencySymbol = segmentSet.CurrencySymbol; } } currentRow.Cells.Add( newCell ); continue; } } //apply similar matches { List<Cell> cells = matches.ConvertAll( col => row.Cells.Find( c => c.Id == col.Id ) ); //Now reduce our cells to those with values... cells.RemoveAll( c => c == null || !c.HasData ); //...and non-duplicates for( int c = 0; c < cells.Count; c++ ) { Cell curCell = cells[ c ]; cells.RemoveAll( cell => cell.Id != curCell.Id && cell.NumericAmount == curCell.NumericAmount ); } switch( cells.Count ) { case 0: Cell emptyCell = new Cell(); currentRow.Cells.Add( emptyCell ); break; case 1: hasData = true; Cell newCell = (Cell)cells[ 0 ].Clone(); newCell.EmbeddedReport = cells[ 0 ].EmbeddedReport; if( !string.IsNullOrEmpty( segmentSet.CurrencyCode ) ) { if( (int)row.Unit == (int)UnitType.Monetary || (int)row.Unit == (int)UnitType.EPS ) { newCell.CurrencyCode = segmentSet.CurrencyCode; newCell.CurrencySymbol = segmentSet.CurrencySymbol; } } currentRow.Cells.Add( newCell ); break; default: Debug.Assert( false, "Too many cells" ); break; } } } //if we actually found data for this row, let's clone the original, and swap out the cells if( hasData ) { InstanceReportRow clonedRow = (InstanceReportRow)row.Clone( false, false ); clonedRow.Cells.AddRange( currentRow.Cells ); clonedRow.Id = segmentCandidate.Rows.Count; segmentCandidate.Rows.Add( clonedRow ); } } //Same as above, don't preserve consecutive abstract rows if( lastAbstract != null ) { int at = segmentCandidate.Rows.IndexOf( lastAbstract ); if( at == segmentCandidate.Rows.Count - 1 ) segmentCandidate.Rows.RemoveAt( at ); } } //now that the permutation is complete, apply the new rows and columns to the "base" report this.Columns.Clear(); this.Columns.AddRange( segmentCandidate.Columns ); this.Rows.Clear(); this.Rows.AddRange( segmentCandidate.Rows ); this.SynchronizeGrid(); //this.InstantValues(); processed.Value = true; return true; }