/// <summary> /// Locates all items in the given collection of facts that match the current element. /// </summary> /// <param name="ItemElement"> /// The element describing the items to be found. /// </param> /// <param name="FactList"> /// The collection of items that should be searched. /// </param> /// <returns> /// A collection of items that match the current element. If no items match, /// a non-null List will still be returned, but the list will be empty. /// </returns> /// <remarks> /// This method should most likely be moved into a class which wraps <see cref="Item"/> /// collections with a value-added wrapper class. /// </remarks> private List <Item> LocateItems(Element ItemElement, FactCollection FactList) { var ItemList = new List <Item>(); if (ItemElement != null) { foreach (Fact CurrentFact in FactList) { if ((CurrentFact is Item) == true) { var CurrentItem = CurrentFact as Item; if (CurrentItem.SchemaElement.Equals(ItemElement) == true) { ItemList.Add(CurrentItem); } } else if ((CurrentFact is Tuple) == true) { var currentTuple = CurrentFact as Tuple; var tupleList = LocateItems(ItemElement, currentTuple.Facts); ItemList.AddRange(tupleList); } } } return(ItemList); }
public static void ParseFacts(XDocument report, Instance instance) { var factElements = FindFacts(report); var facts = new FactCollection(instance); foreach (var factElement in factElements) { var name = factElement.Attribute("name").Value; var prefix = name.Split(':').First(); var ns = report.Root.GetNamespaceOfPrefix(prefix).ToString(); var unitRef = factElement.Attribute("unitRef")?.Value; var decimals = factElement.Attribute("decimals")?.Value; var value = factElement.Value; var scale = factElement.Attribute("scale")?.Value; if (!string.IsNullOrWhiteSpace(scale)) { var power = int.Parse(scale); var v = decimal.Parse(value.Replace(" ", "")); var multiplier = (decimal)Math.Pow(10, power); v *= multiplier; value = v.ToString(); } var contextRef = factElement.Attribute("contextRef").Value; var fact = new Fact(name, ns, unitRef, decimals, contextRef, value); facts.Add(fact); } foreach (var fact in facts) { instance.Facts.Add(fact); } }
/// <summary> /// Validate the essence alias between a given fact and all other facts with the given fact name. /// </summary> /// <remarks> /// <para> /// An essence alias is a relationship between a "from" item and a "to" item. The "from" item and /// the "to" item must be identical. This method is given the "from" item and must search for the /// corresponding "to" item. /// </para> /// <para> /// The scoping of the search for the corresponding "to" item is important. In the simple case, an /// XBRL fragment has only items, and the search for the corresponding "to" item can be conducted /// in the list all of all items in the fragment. /// </para> /// <para> /// However, if the "from" item is found in a tuple, then the list of items from which the "to" item /// is to be found must be restricted to the other items in the tuple and not simply the set of all /// items in the fragment. /// </para> /// </remarks> /// <param name="FromItem"> /// The item that represents the "from" end of the essence alias relationship. /// </param> /// <param name="FactList"> /// The list of facts that should be searched to find the item that represents the "to" end of the essence alias relationship. /// </param> /// <param name="ToItemName"> /// The name of the item that represents the "to" end of the essence alias relationship. /// </param> private void ValidateEssenceAliasedFacts(Item FromItem, FactCollection FactList, string ToItemName) { foreach (Fact CurrentFact in FactList) { var CurrentItem = CurrentFact as Item; if (CurrentItem != null) { if (CurrentFact.Name.Equals(ToItemName) == true) { ValidateEssenceAliasedFacts(FromItem, CurrentItem); } } } }
/// <summary> /// Validate context references for all facts in the given fact collection. /// </summary> /// <param name="FactList"> /// A collection of facts whose contexts should be validated. /// </param> private void ValidateContextRefs(FactCollection FactList) { foreach (Fact CurrentFact in FactList) { if (CurrentFact is Item) { ValidateContextRef(CurrentFact as Item); } else if (CurrentFact is Tuple) { var CurrentTuple = CurrentFact as Tuple; ValidateContextRefs(CurrentTuple.Facts); } } }
/// <summary> /// Locates an item in a list of facts. /// </summary> /// <param name="ItemElement"> /// A schema element defining the item to be found. /// </param> /// <param name="FactList"> /// The collection of items that should be searched. /// </param> /// <returns> /// A reference to the first item that matches the given element, or null if no matching item is found. /// </returns> /// <remarks> /// This method should most likely be moved into a class which wraps <see cref="Item"/> /// collections with a value-added wrapper class. /// </remarks> private Item LocateItem(Element ItemElement, FactCollection FactList) { if (ItemElement == null) { return(null); } foreach (Fact CurrentFact in FactList) { var CurrentItem = CurrentFact as Item; if (CurrentItem != null) { if (CurrentItem.SchemaElement.Equals(ItemElement) == true) { return(CurrentItem); } } } return(null); }
/// <summary> /// Validates a given summation concept. /// </summary> /// <param name="CurrentCalculationLink"> /// The calculation link that defines the given summation concept. /// </param> /// <param name="CurrentSummationConcept"> /// The summation concept to be validated. /// </param> /// <param name="FactList"> /// The collection of items that should be searched when looking for summation or contributing items. /// </param> private void ValidateSummationConcept(CalculationLink CurrentCalculationLink, SummationConcept CurrentSummationConcept, FactCollection FactList) { Element SummationConceptElement = LocateElement(CurrentSummationConcept.SummationConceptLocator); Item SummationConceptItem = LocateItem(SummationConceptElement, FactList); // If the summation concept item doesn't exist, then there is no calculation // to perform. if (SummationConceptItem == null) { return; } // If the summation concept item has a "nil" value, then there is no calculation // to perform. if (SummationConceptItem.NilSpecified == true) { return; } double SummationConceptRoundedValue = SummationConceptItem.RoundedValue; double ContributingConceptRoundedValueTotal = 0; var ContributingConceptItemsFound = false; var AtLeastOneItemWithZeroPrecision = false; foreach (Locator CurrentLocator in CurrentSummationConcept.ContributingConceptLocators) { // Some decisions need to be made before the code can actually add the value of the // contributing concept to the total that the code is keeping. var IncludeContributingConceptItemInCalculation = true; // Find the calculation arc for the given calculation link. CalculationArc ContributingConceptCalculationArc = CurrentCalculationLink.GetCalculationArc(CurrentLocator); if (ContributingConceptCalculationArc == null) { IncludeContributingConceptItemInCalculation = false; } // Find the elemement for the given locator. Element ContributingConceptElement = LocateElement(CurrentLocator); if (ContributingConceptElement == null) { IncludeContributingConceptItemInCalculation = false; } // Find all items for the given element. If there is more than one, and at least // one of them is not p-equals with at least one of the other ones, then // the entire calculation validation is forfeit, according to test 397.12 in // the XBRL-CONF-CR3-2007-03-05 conformance suite. var AllMatchingItems = LocateItems(ContributingConceptElement, FactList); if (AllItemsNotPEquals(AllMatchingItems) == false) { return; } // Find the item for the given element. if (AllMatchingItems.Count == 0) { IncludeContributingConceptItemInCalculation = false; } else { foreach (var ContributingConceptItem in AllMatchingItems) { if (IncludeContributingConceptItemInCalculation == true) { IncludeContributingConceptItemInCalculation = ContributingConceptItemEligibleForUseInCalculation(ContributingConceptItem, SummationConceptItem); } if (IncludeContributingConceptItemInCalculation == true) { ContributingConceptItemsFound = true; if ((ContributingConceptItem.PrecisionSpecified == true) && (ContributingConceptItem.Precision == 0) && (ContributingConceptItem.InfinitePrecision == false)) { AtLeastOneItemWithZeroPrecision = true; } double ContributingConceptRoundedValue = ContributingConceptItem.RoundedValue; if (ContributingConceptCalculationArc.Weight != (decimal)(1.0)) { ContributingConceptRoundedValue = ContributingConceptRoundedValue * (double)(ContributingConceptCalculationArc.Weight); } ContributingConceptRoundedValueTotal += ContributingConceptRoundedValue; } } } } if (ContributingConceptItemsFound == true) { if (AtLeastOneItemWithZeroPrecision == true) { var MessageBuilder = new StringBuilder(); string StringFormat = AssemblyResources.GetName("SummationConceptUsesContributingItemWithPrecisionZero"); MessageBuilder.AppendFormat(StringFormat, SummationConceptItem.Name); ValidatedFragment.AddValidationError(new SummationConceptValidationError(CurrentSummationConcept, MessageBuilder.ToString())); return; } ContributingConceptRoundedValueTotal = SummationConceptItem.Round(ContributingConceptRoundedValueTotal); if (SummationConceptRoundedValue != ContributingConceptRoundedValueTotal) { var MessageBuilder = new StringBuilder(); string StringFormat = AssemblyResources.GetName("SummationConceptError"); MessageBuilder.AppendFormat(StringFormat, SummationConceptItem.Name, SummationConceptRoundedValue, ContributingConceptRoundedValueTotal); ValidatedFragment.AddValidationError(new SummationConceptValidationError(CurrentSummationConcept, MessageBuilder.ToString())); return; } } }
/// <summary> /// Validate the essence alias between two facts referenced in a definition arc using /// the set of all facts in the fragment. /// </summary> /// <param name="EssenceAliasDefinitionArc"> /// The definition arc defining the essence alias. /// </param> /// <param name="FactList"> /// A collection of <see cref="Fact"/> objects defined in the fragment. /// </param> private void ValidateEssenceAliasedFacts(DefinitionArc EssenceAliasDefinitionArc, FactCollection FactList) { Locator CurrentFromLocator = EssenceAliasDefinitionArc.FromLocator; if (CurrentFromLocator == null) { throw new NullReferenceException("FromLocator is NULL in ValidateEssenceAliasedFacts()"); } Locator CurrentToLocator = EssenceAliasDefinitionArc.ToLocator; foreach (Fact CurrentFact in FactList) { if (CurrentFact is Item) { var CurrentItem = CurrentFact as Item; if (CurrentItem.Name.Equals(CurrentFromLocator.HrefResourceId) == true) { ValidateEssenceAliasedFacts(CurrentItem, FactList, CurrentToLocator.HrefResourceId); } } else if (CurrentFact is Tuple) { var CurrentTuple = CurrentFact as Tuple; ValidateEssenceAliasedFacts(EssenceAliasDefinitionArc, CurrentTuple.Facts); } } }