Ejemplo n.º 1
0
        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();
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Reduces the sub tree by keeping only the members with aspects that actually exist in the fact set.
        /// The structure remains a proper, trimmed-down tree, that means parents remain if they have used members.
        /// </summary>
        /// <returns>
        /// The cloned and trimmed sub tree or null of the entire sub tree disappeared.
        /// </returns>
        /// <remarks>
        /// Alternatively, one could use the FactSet.Slice method to decide if the member stays. This avoids the
        /// duplicated predicates but is very wasteful: it calculates the resulting sliced facts sets only for a yes/no decision.
        /// The ideal approach seems to be to isolate the predicate itself for use in both in this method and in FactSet.Slice.
        /// </remarks>
        internal Member Reduce(FactSet facts, Member newParent)
        {
            Func <FactModel, ISet <XName> > factDimensionNames       = fm => new HashSet <XName>(fm.Aspects.OfType <ExplicitMemberAspect>().Select(a => a.Dimension.Name));
            Func <FactModel, bool>          hypercubeDimensionsMatch = fm => HypercubeDimensionNames == null || HypercubeDimensionNames.SetEquals(factDimensionNames(fm));
            Func <FactModel, bool>          containsAspect           = fm => fm.Aspects.Contains(Aspect);

            var clone = Clone(); // Slightly wasteful to clone before knowing if the clone even survives. No biggie though.

            clone.Parent   = newParent;
            clone.Children = Children.Select(ch => ch.Reduce(facts, clone)).Where(m => m != null).ToList();
            return(clone.Children.Any() || facts.FactModels.Any(fm => containsAspect(fm) && hypercubeDimensionsMatch(fm)) ? clone : null);
        }
Ejemplo n.º 3
0
        /// <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();
        }
Ejemplo n.º 4
0
        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);
            }
        }
Ejemplo n.º 5
0
        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();
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Reduces the axis by keeping only the members with aspects that actually exist in the fact set.
        /// The structure remains a proper, trimmed-down tree, that means parents remain if a descendant remains.
        /// </summary>
        public Axis Reduce(FactSet facts)
        {
            var reducedRoots = Roots.Select(m => m.Reduce(facts, null)).Where(m => m != null).ToList();

            return(new Axis(Dimension, Name, reducedRoots));
        }