private void WriteTable(IPresenterWriter presenterWriter, Axis verticalAxis, Axis horizontalAxis, FactSet facts) { // Reduce both axes for a compact display, unless disabled. var reducedVerticalAxis = _settings.IncludeEmptyConcepts ? verticalAxis : verticalAxis.Reduce(facts); var reducedHorizontalAxis = _settings.IncludeEmptyExplicitMembers ? horizontalAxis : horizontalAxis.Reduce(facts); if (!reducedVerticalAxis.Roots.Any()) { throw new InstanceExportException("The reduced vertical axis is empty. This should not happen because them facts must be living somewhere..."); } if (!reducedHorizontalAxis.Roots.Any()) { throw new InstanceExportException("The reduced horizontal axis is empty. This should not happen because them facts must be living somewhere..."); } if (horizontalAxis.Dimension == Dimension.PeriodDimension) { reducedHorizontalAxis = NormalisePeriodAxis(reducedHorizontalAxis); } presenterWriter.WriteBeginTable(reducedHorizontalAxis); var allHorizontalAxisMembers = reducedHorizontalAxis.Linearise(); foreach (var verticalAxisRoot in reducedVerticalAxis.Roots.Cast <ConceptMember>()) { WriteConceptMember(presenterWriter, verticalAxisRoot, reducedVerticalAxis, allHorizontalAxisMembers, facts); } presenterWriter.WriteEndTable(); }
private void WriteConceptMember(IPresenterWriter presenterWriter, ConceptMember conceptMember, Axis verticalAxis, IList <Member> horizontalAxisMembers, FactSet facts) { var hypercubeAxis = conceptMember.HypercubeAxis; if (hypercubeAxis != null) { if (horizontalAxisMembers.Count != 1) { throw new InvalidOperationException($"Hypercubes require a single period, but the hypercube '{conceptMember.Label}' has {horizontalAxisMembers.Count} periods."); } // It is crucial to include related aspects to keep beginning/end of period // facts in the fact set. var periodSlicedFactSet = facts.Slice(horizontalAxisMembers.Single(), includeRelatedAspects: true); var reducedFactSet = periodSlicedFactSet.Reduce(hypercubeAxis); if (!reducedFactSet.FactModels.Any()) { // No fact in this hypercube axis: nothing to show. // This should not happen since the concept axis got reduced already. return; } var verticalChildAxis = verticalAxis.CreateChildAxis(conceptMember); // Clear the hypercube axis to signal consumption and avoid infinite loop. conceptMember.HypercubeAxis = null; WriteTable(presenterWriter, verticalChildAxis, hypercubeAxis, reducedFactSet); return; } var verticallySlicedFactSet = facts.Slice(conceptMember, false); var cellFacts = new Dictionary <Member, FactModel>(); foreach (var horizontalAxisMember in horizontalAxisMembers) { // It is crucial to include related aspects to keep beginning/end of period // facts in the fact set. var horizontallySlicedFactSet = verticallySlicedFactSet.Slice(horizontalAxisMember, true); var cellFact = horizontallySlicedFactSet.GetCellFact(); cellFacts[horizontalAxisMember] = cellFact; } presenterWriter.WriteConcept(conceptMember, cellFacts); foreach (var childMember in conceptMember.Children.Cast <ConceptMember>()) { WriteConceptMember(presenterWriter, childMember, verticalAxis, horizontalAxisMembers, facts); } }
/// <summary> /// Builds up the specified presentation networks by filling in the facts from the XBRL instance. /// The format-specific rendering is done by the writer which assembles the final result. /// </summary> public void Present(IPresenterWriter presenterWriter, ScopeSettings settings) { _settings = settings; var factSet = new FactSet(Instance.Facts); factSet.EnsureNoCollisions(); foreach (var factModel in factSet.FactModels) { factModel.EnsureDistinctDimensions(); factModel.EnsureConsistentPeriodTypes(Instance.Dts); } var standardAxes = BuildStandardAxes(); factSet.EnsureStandardAxesContainFactAspects(standardAxes); presenterWriter.WriteBeginExport(Instance); // 1. Slice by single entity. var entityAxis = FindAxis(standardAxes, Dimension.EntityDimension.Name); var reducedEntityAxis = entityAxis.Reduce(factSet); if (!reducedEntityAxis.Roots.Any()) { throw new InstanceExportException("The reduced entity axis is empty. What the ?!"); } if (reducedEntityAxis.Roots.Count > 1) { throw new InstanceExportException("The instance contains more than one entity. This is not supported."); } var entityMember = entityAxis.Roots[0]; var entityAspect = (EntityAspect)entityMember.Aspect; factSet = factSet.Slice(entityMember, false); // 2. Slice by single currency unit. var unitAxis = FindAxis(standardAxes, Dimension.UnitDimension.Name); var reducedUnitAxis = unitAxis.Reduce(factSet); if (!reducedUnitAxis.Roots.Any()) { throw new InstanceExportException("The reduced unit axis is empty. What the ?!"); } var hasNonCurrencyAspects = reducedUnitAxis.Roots.Select(r => (UnitAspect)r.Aspect).Any(ua => !(ua.Unit is CurrencyUnit)); if (hasNonCurrencyAspects) { throw new InstanceExportException("The instance contains non-currency units. This is not supported yet."); } var currencyMembers = reducedUnitAxis.Roots.Where(m => ((UnitAspect)m.Aspect).Unit is CurrencyUnit).ToList(); if (currencyMembers.Count > 1) { throw new InstanceExportException("The instance contains more than one currency unit. This is not supported."); } var currencyMember = currencyMembers[0]; var currencyAspect = (UnitAspect)currencyMember.Aspect; factSet = factSet.Slice(currencyMember, true); // textual facts will remain because the 'text unit' is related to all currency units. // 3. Determine first duration period. var periodAxis = FindAxis(standardAxes, Dimension.PeriodDimension.Name); var reducedPeriodAxis = periodAxis.Reduce(factSet); if (!reducedPeriodAxis.Roots.Any()) { throw new InstanceExportException("The reduced period axis is empty. What the ?!"); } //var firstDurationAspect = reducedPeriodAxis.Roots.Select(r => (PeriodAspect) r.Aspect).FirstOrDefault(ua => ua.Period is DurationPeriod); //if (firstDurationAspect == null) //{ // throw new XbrlExportException("The instance does not contain any duration period. This is not supported."); //} // Instance looks okay. Ready to write it out. presenterWriter.WriteIntro(entityAspect.Entity, currencyAspect.Unit as CurrencyUnit); WriteNetworks(standardAxes, factSet, settings.PresentationNetworks, presenterWriter); presenterWriter.WriteEndExport(); }
private void WriteNetworks(IList <Axis> standardAxes, FactSet factSet, IList <PresentationNetwork> presentationNetworks, IPresenterWriter presenterWriter) { var periodAxis = FindAxis(standardAxes, Dimension.PeriodDimension.Name); foreach (var presentationNetwork in presentationNetworks) { var presentationLink = Instance.Dts.FindPresentationLink(presentationNetwork.Role); if (presentationLink == null) { throw new InstanceExportException($"There is no presentation network {presentationNetwork.Role} in the DTS."); } var presentationNetworkAxis = BuildPresentationAxis(presentationLink); // Only keep facts in that network. var reducedFactSet = factSet.Reduce(presentationNetworkAxis); if (!reducedFactSet.FactModels.Any()) { // No fact in this network: nothing to show. continue; } presenterWriter.WriteBeginPresentationNetwork(presentationNetwork); WriteTable(presenterWriter, presentationNetworkAxis, periodAxis, reducedFactSet); presenterWriter.WriteEndPresentationNetwork(); } }