public Cell( MarkupProperty mp ) { if( string.IsNullOrEmpty( mp.markupData ) ) this.AddData( NILL_PLACEHOLDER ); else this.AddData( mp.markupData ); if( mp.Links.Count > 0 ) { foreach( FootnoteProperty fnp in mp.Links ) { this.FootnoteIndexer += string.Format( "[{0}],", fnp.Id ); } this.FootnoteIndexer = this.FootnoteIndexer.TrimEnd( ',' ); } this.Markup = mp; this.ContextID = mp.contextRef.ContextID; if( mp.unitRef == null ) this.UnitID = string.Empty; else this.UnitID = mp.unitRef.UnitID; }
internal static bool RecursivelyBuildTupleFromXML(XmlNode node, ArrayList contexts, ArrayList units, TupleSet parentTupleSet, ref ArrayList markups, ref ArrayList errors) { for (int i = 0; i < node.ChildNodes.Count; ++i) { XmlNode child = node.ChildNodes[i]; if (child is XmlElement) { if (child.FirstChild == null || (!(child.FirstChild is XmlComment) && child.FirstChild.Value != null)) { //child is a markup MarkupProperty mp = null; if (!MarkupProperty.TryCreateFromXml(i, node.ChildNodes, contexts, units, out mp, ref errors)) { continue; } mp.TupleSetName = parentTupleSet.Name; mp.xmlElement = child; mp.TupleParentList = new List <string>(); parentTupleSet.GetTupleParentList(ref mp.TupleParentList); markups.Add(mp); parentTupleSet.AddChild(mp); } else { TupleSet childTupleSet = CreateInstanceDocumentTupleSet(child); if (childTupleSet != null) { childTupleSet.ParentSet = parentTupleSet; parentTupleSet.AddChild(childTupleSet); if (!RecursivelyBuildTupleFromXML(child, contexts, units, childTupleSet, ref markups, ref errors)) { return(false); } } } } } return(true); }
public void TestApplyScaleToMarkup() { MarkupProperty mp = new MarkupProperty(); mp.markupData = "1234"; // test 1 - no scale mp.unitRef = null; Assert.AreEqual("1234", ApplyScaleToMarkup(mp), "Incorrect value with no scale"); mp.unitRef = new UnitProperty(); mp.unitRef.Scale = 0; mp.markupData = "1"; Assert.AreEqual("1", ApplyScaleToMarkup(mp) ); mp.unitRef.Scale = 1; mp.markupData = "1"; Assert.AreEqual("10", ApplyScaleToMarkup(mp) ); mp.unitRef.Scale = 2; mp.markupData = "1"; Assert.AreEqual("100", ApplyScaleToMarkup(mp) ); mp.unitRef.Scale = 2; mp.markupData = "1"; Assert.AreEqual("100", ApplyScaleToMarkup(mp) ); mp.unitRef.Scale = 10; mp.markupData = "1"; // 1234567890 Assert.AreEqual("10000000000", ApplyScaleToMarkup(mp) ); mp.unitRef.Scale = 6; mp.markupData = "12345.26"; Assert.AreEqual("12345260000", ApplyScaleToMarkup(mp) ); mp.unitRef.Scale = 2; mp.markupData = "1.11"; Assert.AreEqual("111", ApplyScaleToMarkup(mp) ); mp.unitRef.Scale = 1; mp.markupData = "123.11"; Assert.AreEqual("1231", ApplyScaleToMarkup(mp) ); }
internal void UpdateMarkupsWithTupleSetInformation(TupleSet root, List <string> parentList) { List <string> totalParentList = new List <string>(); totalParentList.Add(this.TupleParentElementId); totalParentList.AddRange(parentList); foreach (ITupleSetChild child in this.Children.Values) { if (child is MarkupProperty) { MarkupProperty mp = child as MarkupProperty; mp.TopLevelTupleset = root; mp.TupleParentList = totalParentList; mp.TupleSetName = this.Name; } else if (child is TupleSet) { (child as TupleSet).UpdateMarkupsWithTupleSetInformation(root, totalParentList); } } }
internal void FixPrefix(Dictionary <string, string> prefixMap) { foreach (KeyValuePair <string, string> kvp in prefixMap) { if (this.TupleParentElementId.StartsWith(kvp.Key)) { TupleParentElementId = kvp.Value + TupleParentElementId.Substring(kvp.Key.Length); break; } } foreach (ITupleSetChild child in this.Children.Values) { TupleSet childSet = child as TupleSet; if (childSet != null) { childSet.FixPrefix(prefixMap); } else { MarkupProperty mp = child as MarkupProperty; if (mp != null && mp.TupleParentList != null) { for (int i = 0; i < mp.TupleParentList.Count; i++) { foreach (KeyValuePair <string, string> kvp in prefixMap) { if (mp.TupleParentList[i].StartsWith(kvp.Key)) { mp.TupleParentList[i] = kvp.Value + mp.TupleParentList[i].Substring(kvp.Key.Length); break; } } } } } } }
/// <summary> /// when we read tuple from instance document /// we get a colon that separates prefix from the name /// we need it to be an underscore to make it work with the element id /// </summary> public void ConvertColonToUnderScore() { this.TupleParentElementId = this.TupleParentElementId.Replace(":", "_"); foreach (ITupleSetChild child in this.Children.Values) { TupleSet childSet = child as TupleSet; if (childSet != null) { childSet.ConvertColonToUnderScore(); } else { MarkupProperty mp = child as MarkupProperty; if (mp != null && mp.TupleParentList != null) { for (int i = 0; i < mp.TupleParentList.Count; i++) { mp.TupleParentList[i] = mp.TupleParentList[i].Replace(":", "_"); } } } } }
/// <summary> /// Determines whether a supplied <see cref="Object"/> is equal to this <see cref="MarkupProperty"/>. /// </summary> /// <param name="obj">The <see cref="Object"/> to be compared to this <see cref="MarkupProperty"/>. /// Assumed to be a <see cref="MarkupProperty"/>.</param> /// <returns>True if <paramref name="obj"/> is equal to this <see cref="MarkupProperty"/>.</returns> /// <remarks>To be equal, the tuple set name, element id, and context reference /// properties in the two <see cref="MarkupProperty"/> objects must be equal.</remarks> public override bool Equals(object obj) { if (!(obj is MarkupProperty)) { return(false); } MarkupProperty mp = (MarkupProperty)obj; if (TupleSetName != null) { if (mp.TupleSetName == null) { return(false); } if (string.Compare(TupleSetName, mp.TupleSetName) != 0) { return(false); } } else if (mp.TupleSetName != null && mp.TupleSetName != string.Empty) { return(false); } if (elementId.CompareTo(mp.elementId) != 0) { return(false); } if (!contextRef.ValueEquals(mp.contextRef)) { return(false); } return(true); }
public void CreateFromXml() { string startXml = @"<?xml version=""1.0"" encoding=""utf-16""?> <?xml-stylesheet type=""text/xsl"" href=""Test.xsl""?> <!--XBRLParser message--> <!--Based on XBRL 2.1--> <!--Created on SomeDate--> <xbrl xmlns=""http://www.xbrl.org/2003/instance"" xmlns:link=""http://www.xbrl.org/2003/linkbase"" xmlns:xlink=""http://www.w3.org/1999/xlink"" xmlns:usfr_pt=""http://www.xbrl.org/2003/usfr"" xmlns:iso4217=""http://www.xbrl.org/2003/iso4217""> <link:schemaRef xlink:type=""simple"" xlink:href=""test.xsd"" /> <!--Context Section--> <context id=""current_AsOf""> <entity> <identifier scheme=""http://www.rivetsoftware.com"">Rivet</identifier> </entity> <period> <instant>2003-12-31</instant> </period> </context> <!--Unit Section--> <unit id=""u1""> <!--Scale: 2--> <measure>iso4217:USD</measure> </unit> <!--Element Section--> <!--Document address: Excel - Sheet1!$A$1--> <usfr_pt:element1 contextRef=""current_AsOf"" unitRef=""u1"" decimals=""10"">1234500</usfr_pt:element1> </xbrl>"; XmlDocument xDoc = new XmlDocument(); xDoc.LoadXml( startXml ); XmlNamespaceManager theManager = new XmlNamespaceManager( xDoc.NameTable ); theManager.AddNamespace( "link2", "http://www.xbrl.org/2003/instance" ); theManager.AddNamespace( "usfr_pt", "http://www.xbrl.org/2003/usfr" ); ContextProperty cp = new ContextProperty( "current_AsOf" ); cp.EntityValue = "Rivet"; cp.EntitySchema = "http://www.rivetsoftware.com"; cp.PeriodType = Element.PeriodType.instant; cp.PeriodStartDate = new DateTime( 2003, 12, 31 ); ArrayList contexts = new ArrayList( ); contexts.Add( cp ); UnitProperty up = new UnitProperty( "u1", UnitProperty.UnitTypeCode.Standard ); up.StandardMeasure.MeasureNamespace = "iso4217"; up.StandardMeasure.MeasureSchema = "http://www.xbrl.org/2003/iso4217"; up.StandardMeasure.MeasureValue = "USD"; MarkupProperty realMP = new MarkupProperty(); realMP.contextRef = cp; realMP.unitRef = up; realMP.elementId = "usfr_pt_element1"; realMP.element = new Node( new Element( "element1" ) ); realMP.markupData = "1234500"; realMP.precisionDef = new Precision( Precision.PrecisionTypeCode.Decimals, 10 ); ArrayList units = new ArrayList(); units.Add( up ); XmlNodeList elemList = xDoc.SelectNodes( "//usfr_pt:element1", theManager ); Assert.AreEqual( 1, elemList.Count, "elem not found" ); MarkupProperty mp = null; ArrayList errors = new ArrayList(); Assert.IsTrue( MarkupProperty.TryCreateFromXml( 0, elemList, contexts, units, out mp, ref errors ), "markup property not created" ); Assert.IsTrue( mp.Equals( realMP ), "elem different from the real deal" ); Assert.AreEqual( "http://www.xbrl.org/2003/usfr", mp.elementNamespace, "namespace wrong" ); }
public void TestIsMarkupMorePreciseThanRoundedValue() { MarkupProperty mp = new MarkupProperty(); mp.markupData = "123456"; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, -3); mp.unitRef = new UnitProperty(); Assert.IsTrue(mp.IsMarkupMorePreciseThanRoundedValue()); mp.unitRef.Scale = 2; Assert.IsTrue(mp.IsMarkupMorePreciseThanRoundedValue()); mp.unitRef.Scale = 3; Assert.IsFalse(mp.IsMarkupMorePreciseThanRoundedValue()); mp.markupData = "123456000"; mp.unitRef.Scale = 0; Assert.IsFalse(mp.IsMarkupMorePreciseThanRoundedValue()); mp.markupData = "126322"; mp.unitRef.Scale = 4; Assert.IsFalse(mp.IsMarkupMorePreciseThanRoundedValue()); mp.markupData = "126322.3"; mp.unitRef.Scale = 4; Assert.IsFalse(mp.IsMarkupMorePreciseThanRoundedValue()); mp.markupData = "126322.3"; mp.unitRef.Scale = 3; Assert.IsTrue(mp.IsMarkupMorePreciseThanRoundedValue()); mp.markupData = "567456498"; mp.unitRef.Scale = 0; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 0); Assert.IsFalse(mp.IsMarkupMorePreciseThanRoundedValue()); //some per share info mp.markupData = "1.99"; mp.unitRef.Scale = 0; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 2); Assert.IsFalse(mp.IsMarkupMorePreciseThanRoundedValue()); mp.markupData = "0.99"; mp.unitRef.Scale = 0; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 2); Assert.IsFalse(mp.IsMarkupMorePreciseThanRoundedValue()); mp.markupData = "1.991"; mp.unitRef.Scale = 0; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 2); Assert.IsTrue(mp.IsMarkupMorePreciseThanRoundedValue()); mp.markupData = "9.991"; mp.unitRef.Scale = 0; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 2); Assert.IsTrue(mp.IsMarkupMorePreciseThanRoundedValue()); mp.markupData = "476403"; mp.unitRef.Scale = 3; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, -6); Assert.IsTrue(mp.IsMarkupMorePreciseThanRoundedValue()); }
public void TestGetRoundedMarkedupAmount() { MarkupProperty mp = new MarkupProperty(); mp.markupData = "123456.789012"; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, -3); Assert.AreEqual(123000, mp.GetRoundedMarkedupAmount()); mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, -2); Assert.AreEqual(123500, mp.GetRoundedMarkedupAmount()); mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 0); Assert.AreEqual(123457, mp.GetRoundedMarkedupAmount()); mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 2); Assert.AreEqual(123456.79, mp.GetRoundedMarkedupAmount()); mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 4); Assert.AreEqual(123456.7890, mp.GetRoundedMarkedupAmount()); mp.markupData = "(123,456.789012)"; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, -2); Assert.AreEqual(-123500, mp.GetRoundedMarkedupAmount()); mp.markupData = "22.98"; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.None); mp.unitRef = new UnitProperty(); decimal sum = mp.GetRoundedMarkedupAmount(); Console.WriteLine(sum); mp.markupData = "3.478"; sum += mp.GetRoundedMarkedupAmount(); Assert.AreEqual(26.458, sum); //with double we get floating point error.... Console.WriteLine(sum); mp.markupData = "2.3"; sum += mp.GetRoundedMarkedupAmount(); Console.WriteLine(sum); mp.markupData = "3.478"; sum += mp.GetRoundedMarkedupAmount(); Console.WriteLine(sum); mp.markupData = "345000012"; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 0); Console.WriteLine(mp.GetRoundedMarkedupAmount()); mp.markupData = "34501.67"; mp.precisionDef = new Precision(Precision.PrecisionTypeCode.Decimals, 0); Console.WriteLine(mp.GetRoundedMarkedupAmount()); }
/// <summary> /// Connects footnotes to a markup, using the markup's element id. /// </summary> private void ConnectFootnotes(MarkupProperty mp, XmlAttribute elementID, Dictionary<string, List<FootnoteProperty>> footnoteInfos ) { string id = elementID.Value; List<FootnoteProperty> relatedFootnotes; if (footnoteInfos.TryGetValue(id, out relatedFootnotes)) { if (mp.Id == null) mp.Id = elementID.Value; //add the id to the arraylist (to check for dup IDs in FilterMarkups) int idx = mpIDs.BinarySearch(mp.Id); if (idx < 0) { mpIDs.Insert(~idx, mp.Id); } foreach (FootnoteProperty fp in relatedFootnotes) { //link the footnoteProperty to the markupProperty mp.Link(fp); } } }
/// <summary> /// Ensures that the <see cref="UnitProperty"/> for a given <see cref="MarkupProperty"/> /// has the same value as the equivalent <see cref="UnitProperty"/> is the units collection /// of this <see cref="Instance"/>. If the <see cref="UnitProperty"/> for the given /// <see cref="MarkupProperty"/> does not exists in the units collection, it will be added. /// If the unit ids of the two are not the same, the one in the units collection will be /// updated. /// </summary> /// <param name="mp">The <see cref="MarkupProperty"/> whose <see cref="UnitProperty"/> is /// to be checked.</param> protected void CheckDuplicateUnits( MarkupProperty mp ) { UnitProperty up = mp.unitRef; // check to see if it's used int upIndex = units.BinarySearch( up.UnitID ); if ( upIndex > -1 ) { // yep, the unit is used, now check to ensure the units have the same internal data UnitProperty existingUP = (UnitProperty)units[upIndex]; if ( !existingUP.ValueEquals( up ) ) { //see if any of the other units can be used bool createNew = true; foreach (UnitProperty u in units) { if (up.ValueEquals(u)) { up.UnitID = u.UnitID; createNew = false; break; } } if (createNew) { up.UnitID = CreateUniqueName(up.UnitID, units); } } } else { units.Insert( ~upIndex, up ); } }
internal static bool TryCreateFromInlineXbrl(XmlNode data, string inLineXBRLPrefix, string xbrliPrefix, Dictionary<string, string> namespaceused, ArrayList contexts, ArrayList units, bool isNumeric, Dictionary<string, Dictionary<decimal, ITupleSetChild>> tupleMarkups, out MarkupProperty mp, ref ArrayList errors) { foreach (XmlAttribute attr in data.Attributes) { if (attr.Value.ToLower().Equals(DocumentBase.INLINE_XBRL_URI.ToLower())) { inLineXBRLPrefix = attr.LocalName; } else if (attr.Value.ToLower().Equals(DocumentBase.XBRL_INSTANCE_URL.ToLower())) { xbrliPrefix = attr.LocalName; } } mp = new MarkupProperty(); if (data.Attributes[inLineXBRLPrefix + ":order"] != null && data.Attributes[inLineXBRLPrefix + ":tupleRef"] == null) { //these are tuple children that are nested inside tuples... //better evaluate it with the tuple..... return false; } /* <ix:nonFraction ix:name="us-gaap:SellingAndMarketingExpense" ix:format="commadot" ix:scale="6" decimals="-6" unitRef="usd" contextRef="c20080331_9_cik_00009869________">9,161 </ix:nonFraction> */ XmlAttribute nameAttr = data.Attributes[ inLineXBRLPrefix + ":name"]; if (nameAttr == null) { nameAttr = data.Attributes["name"]; } if (nameAttr == null) { errors.Add("Failed to parse " + data.OuterXml); return false; } // first get the name string[] nameParts = nameAttr.Value.Split(':'); //TODO: need to parse this information //mp.elementNamespace = data.NamespaceURI; mp.elementPrefix = nameParts[0]; mp.elementName = nameParts[1]; mp.elementId = string.Format(DocumentBase.ID_FORMAT, nameParts[0], nameParts[1]); namespaceused.TryGetValue(mp.elementPrefix, out mp.elementNamespace); mp.element = new Node(new Element(mp.elementName, mp.elementId, false)); mp.element.MyElement.HasCompleteTupleFamily = false; // try and get the context ref XmlAttribute contextAttr = data.Attributes[Instance.CONTEXT_REF]; if (contextAttr == null) { contextAttr = data.Attributes[inLineXBRLPrefix + ":" + Instance.CONTEXT_REF]; } if (contextAttr == null) { Common.WriteError("XBRLParser.Error.ElementMissingRequiredField", errors, Instance.CONTEXT_REF); return false; } string contextId = contextAttr.Value; // make sure we can find the real context int contextIndex = contexts.BinarySearch(contextId); if (contextIndex < 0) { Common.WriteError("XBRLParser.Error.ContextIdNotFound", errors, contextId); return false; } mp.SetContext((ContextProperty)contexts[contextIndex]); if (isNumeric) { // and get the unit XmlAttribute unitAttr = data.Attributes[Instance.UNIT_REF]; if (unitAttr == null) { unitAttr = data.Attributes[inLineXBRLPrefix + ":" + Instance.UNIT_REF]; } if (unitAttr != null) { string unitId = unitAttr.Value; // make sure we can find the real context int unitIndex = units.BinarySearch(unitId); if (unitIndex < 0) { Common.WriteError("XBRLParser.Error.UnitIdNotFound", errors, unitId); return false; } mp.SetUnit((UnitProperty)units[unitIndex]); // and precision XmlAttribute attr = data.Attributes[Precision.DECIMALS]; if (attr == null) { attr = data.Attributes[xbrliPrefix + ":" + Precision.DECIMALS]; } if (attr == null) { attr = data.Attributes[Precision.PRECISION]; } if (attr == null) { attr = data.Attributes[xbrliPrefix + ":" + Precision.PRECISION]; } if (attr != null) { Precision p = null; if (!Precision.TryCreateFromXml(attr, out p, ref errors)) { return false; } mp.SetPrecision(p); } } } mp.markupData = data.InnerText; string formatvalue = string.Empty; XmlAttribute formatAttr = data.Attributes[inLineXBRLPrefix + ":format"]; if (formatAttr != null) { formatvalue = formatAttr.Value; string[] split = formatvalue.Split(':'); if (split.Length > 1) { formatvalue = split[1]; } } if ( isNumeric ) { int scaleVal = 0; bool isNegated = false; XmlAttribute scaleAttr = data.Attributes[inLineXBRLPrefix + ":scale"]; if (scaleAttr != null) { int.TryParse(scaleAttr.Value, out scaleVal); } if (data.Attributes[inLineXBRLPrefix + ":sign"] != null) { isNegated = true; } if (!TryConvertInlineXbrlNonFraction(scaleVal, isNegated, formatvalue, ref mp.markupData)) { errors.Add("Failed to parse element" + data.OuterXml); return false; } } if (data.Attributes[ inLineXBRLPrefix + ":order"] != null && data.Attributes[ inLineXBRLPrefix + ":tupleRef"] != null) { decimal order = 0; if (decimal.TryParse(data.Attributes[inLineXBRLPrefix + ":order"].Value, out order)) { string tupleId = data.Attributes[inLineXBRLPrefix + ":tupleRef"].Value; Dictionary<decimal, ITupleSetChild> inner; if (!tupleMarkups.TryGetValue(tupleId, out inner)) { inner = new Dictionary<decimal, ITupleSetChild>(); tupleMarkups[tupleId] = inner; } inner[order] = mp; } else { errors.Add("Failed to parse ix:order information for element" + data.OuterXml); return false; } } return true; }
/// <summary> /// Creates and populates a new <see cref="MarkupProperty"/>. /// </summary> /// <param name="index">An index into <paramref name="nodes"/> indicating the <see cref="XmlNode"/> that is to be the /// source of information for populating <paramref name="mp"/>.</param> /// <param name="nodes">The <see cref="XmlNodeList"/> containing the <XmlNode> from which <paramref name="mp"/> is /// to be created.</XmlNode></param> /// <param name="contexts">Collection of <see cref="ContextProperty"/> to be assigned to <paramref name="mp"/>.</param> /// <param name="units">Collection of <see cref="UnitProperty"/> to be assigned to <paramref name="mp"/>.</param> /// <param name="mp">An output parameter. The newly created <see cref="MarkupProperty"/>.</param> /// <param name="errors"></param> /// <returns>True if <paramref name="mp"/> could be successfully created and populated.</returns> public static bool TryCreateFromXml( int index, XmlNodeList nodes, ArrayList contexts, ArrayList units, out MarkupProperty mp, ref ArrayList errors ) { XmlNode data = nodes[index]; mp = new MarkupProperty(); if ( errors == null ) { errors = new ArrayList(); } // first get the name string[] nameParts = data.Name.Split( ':' ); mp.elementNamespace = data.NamespaceURI; mp.elementPrefix = nameParts[0]; mp.elementName = nameParts[1]; mp.elementId = string.Format(DocumentBase.ID_FORMAT, nameParts[0], nameParts[1]); mp.element = new Node( new Element( mp.elementName, mp.elementId, false ) ); mp.element.MyElement.HasCompleteTupleFamily = false; // try and get the context ref XmlAttribute contextAttr = data.Attributes[ Instance.CONTEXT_REF ]; if ( contextAttr == null ) { Common.WriteError( "XBRLParser.Error.ElementMissingRequiredField", errors, Instance.CONTEXT_REF ); return false; } string contextId = contextAttr.Value; // make sure we can find the real context int contextIndex = contexts.BinarySearch( contextId ); if ( contextIndex < 0 ) { Common.WriteError( "XBRLParser.Error.ContextIdNotFound", errors, contextId ); return false; } mp.SetContext( (ContextProperty)contexts[contextIndex] ); // and get the unit XmlAttribute unitAttr = data.Attributes[ Instance.UNIT_REF ]; if ( unitAttr != null ) { string unitId = unitAttr.Value; // make sure we can find the real context int unitIndex = units.BinarySearch( unitId ); if ( unitIndex < 0 ) { Common.WriteError( "XBRLParser.Error.UnitIdNotFound", errors, unitId ); return false; } mp.SetUnit( (UnitProperty)units[unitIndex] ); // and precision XmlAttribute attr = data.Attributes[ Precision.DECIMALS ]; if ( attr == null ) { attr = data.Attributes[ Precision.PRECISION ]; } if ( attr != null ) { Precision p = null; if ( !Precision.TryCreateFromXml( attr, out p, ref errors ) ) { return false; } mp.SetPrecision( p ); } } mp.markupData = data.InnerText; mp.AddXmlComments( index, nodes ); return true; }
public static Precision WritePrecision( MarkupProperty markup, ref int previousPrecision, ref int previousPrecisionSegmented, out bool hasSegments ) { hasSegments = false; if( markup.precisionDef == null ) return null; if( markup.contextRef.Segments != null && markup.contextRef.Segments.Count > 0 ) hasSegments = true; int oldPrecision = 0; int newPrecision = markup.precisionDef.NumberOfDigits; if( hasSegments ) { oldPrecision = previousPrecisionSegmented; newPrecision = Math.Max( oldPrecision, newPrecision ); if( newPrecision > oldPrecision ) { previousPrecisionSegmented = newPrecision; return markup.precisionDef; } } else { oldPrecision = previousPrecision; newPrecision = Math.Max( oldPrecision, newPrecision ); if( newPrecision > oldPrecision ) { previousPrecision = newPrecision; return markup.precisionDef; } } return null; }
public void Test_AddFootnoteResources() { xDoc = new XmlDocument(); xDoc.AppendChild( xDoc.CreateXmlDeclaration( "1.0", "utf-16", null ) ); XmlElement root = xDoc.CreateElement( XBRL ); xDoc.AppendChild( root ); root.SetAttribute(DocumentBase.XMLNS, DocumentBase.XBRL_INSTANCE_URL); root.SetAttribute(string.Format(DocumentBase.NAME_FORMAT, DocumentBase.XMLNS, DocumentBase.XBRL_LINKBASE_PREFIX), DocumentBase.XBRL_LINKBASE_URL); root.SetAttribute(string.Format(DocumentBase.NAME_FORMAT, DocumentBase.XMLNS, DocumentBase.XLINK_PREFIX), DocumentBase.XLINK_URI); CreateInstanceSkeleton( root, true, true ); ArrayList mps = new ArrayList(); // markup property MarkupProperty mp = new MarkupProperty(); mp.Id = "Item-01"; mp.address = "$c$6"; mp.Link( new FootnoteProperty( "Footnote-01", "$c$7", "en", "this is the footnote" ) ); mp.xmlElement = xDoc.CreateElement( "usfr_pt:element1" ); mps.Add( mp ); // markup property 2 MarkupProperty mp2 = new MarkupProperty(); mp2.Id = "Item-02"; mp2.address = "$b$6"; mp2.Link( new FootnoteProperty( "Footnote-02", "$c$8", "en", "this is the footnote2" ) ); mp2.xmlElement = xDoc.CreateElement( "usfr_pt:element2" ); mps.Add( mp2 ); AddFootnoteLocatorsAndArcs( mps ); AddFootnoteResources( mps ); System.IO.StringWriter xml = new System.IO.StringWriter(); xDoc.Save( xml ); Console.WriteLine( xml.ToString() ); string expectedXml = @"<?xml version=""1.0"" encoding=""utf-16""?> <xbrl xmlns=""http://www.xbrl.org/2003/instance"" xmlns:link=""http://www.xbrl.org/2003/linkbase"" xmlns:xlink=""http://www.w3.org/1999/xlink""> <!--Context Section--> <!--Unit Section--> <!--Tuple Section--> <!--Element Section--> <!--Footnote Section--> <link:footnoteLink xlink:type=""extended"" xlink:role=""http://www.xbrl.org/2003/role/link""> <!--Document address: $c$6 - Element Name: usfr_pt:element1--> <link:loc xlink:type=""locator"" xlink:href=""#Item-01"" xlink:label=""Item-01_lbl"" /> <link:footnoteArc xlink:type=""arc"" xlink:arcrole=""http://www.xbrl.org/2003/arcrole/fact-footnote"" xlink:from=""Item-01_lbl"" xlink:to=""Footnote-01"" order=""1"" /> <!--Document address: $b$6 - Element Name: usfr_pt:element2--> <link:loc xlink:type=""locator"" xlink:href=""#Item-02"" xlink:label=""Item-02_lbl"" /> <link:footnoteArc xlink:type=""arc"" xlink:arcrole=""http://www.xbrl.org/2003/arcrole/fact-footnote"" xlink:from=""Item-02_lbl"" xlink:to=""Footnote-02"" order=""1"" /> <!--Document address: $c$7--> <link:footnote xlink:type=""resource"" xlink:role=""http://www.xbrl.org/2003/role/footnote"" xlink:label=""Footnote-01"" xml:lang=""en"">this is the footnote</link:footnote> <!--Document address: $c$8--> <link:footnote xlink:type=""resource"" xlink:role=""http://www.xbrl.org/2003/role/footnote"" xlink:label=""Footnote-02"" xml:lang=""en"">this is the footnote2</link:footnote> </link:footnoteLink> </xbrl>"; if( System.Environment.OSVersion.Platform.ToString() == "128") { expectedXml = expectedXml.Replace( "\r\n", "\n"); } Assert.AreEqual( expectedXml, xml.ToString() ); }
/// <summary> /// Validates key properties of a given <see cref="MarkupProperty"/>. /// </summary> /// <param name="mp">The <see cref="MarkupProperty"/> to be validated.</param> /// <param name="errors">A collection of <see cref="String"/> objects to which method /// will append any validation issues.</param> /// <returns>True if the properties of <paramref name="mp"/> are value. False otherwise.</returns> protected bool TryValidateMarkup( MarkupProperty mp, ArrayList errors ) { if ( mp.element == null || mp.contextRef == null ) { Common.WriteError( string.Format( "Internal error: markup hashtable (address: {0}) contains a null {1}", mp.address, mp.element==null ? "element" : "context" ), errors ); return false; } if ( mp.taxonomyIndex == -1 ) { Common.WriteError( string.Format( "Internal error: markup object located at address {0} containing element name {1} contains an incorrect taxonomy index", mp.address, mp.element.Label ), errors ); return false; } return true; }
/// <summary> /// Removes a parameter supplied <see cref="MarkupProperty"/>, including related /// comments, from its parent <see cref="XmlNode"/>. /// </summary> /// <param name="mp">The <see cref="MarkupProperty"/> to be removed.</param> /// <remarks>If, by the deletion, the parent is empty, it will be removed from /// it parent.</remarks> protected void RemoveMarkupInfo( MarkupProperty mp ) { if (mp == null) return; XmlNode parent = mp.xmlElement.ParentNode; if (parent != null) { parent.RemoveChild( mp.xmlElement ); } mp.xmlElement = null; foreach ( XmlComment comment in mp.xmlComments ) { if (comment.ParentNode != null) { comment.ParentNode.RemoveChild( comment ); } } mp.xmlComments.Clear(); if ( mp.oldXmlComments.Count > 0 ) { foreach ( XmlComment comment in mp.oldXmlComments ) { if (comment.ParentNode != null) { comment.ParentNode.RemoveChild( comment ); } } mp.oldXmlComments.Clear(); } // I don't think this is necessary. By definition, a markup property is equal if it's element name // and context property are equal - once a context is used, it will never be removed // mp.contextRef.RemoveXmlNode(); // but units are a different story if (mp.unitRef != null) mp.unitRef.RemoveXmlNode(); // BUG 1143 - if there are no child nodes then delete the parent too (clean up the empty tuple parent) if (parent != null && !parent.HasChildNodes) { XmlNode grandParent = parent.ParentNode; if (grandParent != null) { grandParent.RemoveChild( parent ); } } }
/// <summary> /// /// </summary> /// <param name="mp"></param> /// <param name="parentElement"></param> protected void AddFootnoteLocatorAndArcs(MarkupProperty mp, XmlElement parentElement) { if (mp.LinkCount > 0) { // add the locator AddFootnoteLocator(mp, parentElement); } // and add the arcs int order = 1; foreach (LinkableProperty lp in mp.Links) { AddFootnoteArc(mp.Id, lp.Id, order++, parentElement); } }
/// <summary> /// Creates and populates a new <see cref="MarkupProperty"/>. /// </summary> /// <param name="index">An index into <paramref name="nodes"/> indicating the <see cref="XmlNode"/> that is to be the /// source of information for populating <paramref name="mp"/>.</param> /// <param name="nodes">The <see cref="XmlNodeList"/> containing the <XmlNode> from which <paramref name="mp"/> is /// to be created.</XmlNode></param> /// <param name="contexts">Collection of <see cref="ContextProperty"/> to be assigned to <paramref name="mp"/>.</param> /// <param name="units">Collection of <see cref="UnitProperty"/> to be assigned to <paramref name="mp"/>.</param> /// <param name="mp">An output parameter. The newly created <see cref="MarkupProperty"/>.</param> /// <param name="errors"></param> /// <returns>True if <paramref name="mp"/> could be successfully created and populated.</returns> public static bool TryCreateFromXml(int index, XmlNodeList nodes, ArrayList contexts, ArrayList units, out MarkupProperty mp, ref ArrayList errors) { XmlNode data = nodes[index]; mp = new MarkupProperty(); if (errors == null) { errors = new ArrayList(); } // first get the name string[] nameParts = data.Name.Split(':'); mp.elementNamespace = data.NamespaceURI; mp.elementPrefix = nameParts[0]; mp.elementName = nameParts[1]; mp.elementId = string.Format(DocumentBase.ID_FORMAT, nameParts[0], nameParts[1]); mp.element = new Node(new Element(mp.elementName, mp.elementId, false)); mp.element.MyElement.HasCompleteTupleFamily = false; // try and get the context ref XmlAttribute contextAttr = data.Attributes[Instance.CONTEXT_REF]; if (contextAttr == null) { Common.WriteError("XBRLParser.Error.ElementMissingRequiredField", errors, Instance.CONTEXT_REF); return(false); } string contextId = contextAttr.Value; // make sure we can find the real context int contextIndex = contexts.BinarySearch(contextId); if (contextIndex < 0) { Common.WriteError("XBRLParser.Error.ContextIdNotFound", errors, contextId); return(false); } mp.SetContext((ContextProperty)contexts[contextIndex]); // and get the unit XmlAttribute unitAttr = data.Attributes[Instance.UNIT_REF]; if (unitAttr != null) { string unitId = unitAttr.Value; // make sure we can find the real context int unitIndex = units.BinarySearch(unitId); if (unitIndex < 0) { Common.WriteError("XBRLParser.Error.UnitIdNotFound", errors, unitId); return(false); } mp.SetUnit((UnitProperty)units[unitIndex]); // and precision XmlAttribute attr = data.Attributes[Precision.DECIMALS]; if (attr == null) { attr = data.Attributes[Precision.PRECISION]; } if (attr != null) { Precision p = null; if (!Precision.TryCreateFromXml(attr, out p, ref errors)) { return(false); } mp.SetPrecision(p); } } mp.markupData = data.InnerText; mp.AddXmlComments(index, nodes); return(true); }
/// <summary> /// Compares this instance of <see cref="MarkupProperty"/> to a supplied <see cref="Object"/>. /// </summary> /// <param name="obj">An <see cref="object"/> to which this instance of <see cref="MarkupProperty"/> /// is to be compared. Assumed to be a <see cref="MarkupProperty"/>.</param> /// <returns>An <see cref="int"/> indicating if <paramref name="obj"/> is less than (<0), /// greater than (>0), or equal to (0) this instance of <see cref="MarkupProperty"/>.</returns> /// <remarks>The following <see cref="ContextProperty"/> properties are compared in order: /// <bl> /// <li>Element Id.</li> /// <li>Context reference.</li> /// <li>Unit ID.</li> /// <li>Tuple Set Name.</li> /// </bl> /// </remarks> public int CompareTo(object obj) { if (obj is string) { return(elementId.CompareTo((string)obj)); } MarkupProperty mpObj = (MarkupProperty)obj; int nameCompare = 0; if (elementId != null) { nameCompare = elementId.CompareTo(mpObj.elementId); } else { nameCompare = string.Compare(string.Empty, mpObj.elementId); } if (nameCompare == 0) { int contextCompare = contextRef.CompareValue(mpObj.contextRef); if (contextCompare == 0) { //also check units //BUG 1418 - make sure units are not null if (unitRef == null || unitRef.UnitID == null || mpObj.unitRef == null || mpObj.unitRef.UnitID == null) { //and check tupleset names if (TupleSetName == null && (mpObj.TupleSetName == null || mpObj.TupleSetName == string.Empty)) { return(contextCompare); } else if (TupleSetName == null && mpObj.TupleSetName != null) { //tupleSetNames not equal, so don't return 0 return(-1); } else { return(TupleSetName.CompareTo(mpObj.TupleSetName)); } } // DE1433 we were just comparing the UnitID's here and not the complete value of the unitRef. int unitCompare = unitRef.CompareValue(mpObj.unitRef); if (unitCompare == 0) { //check tupleset names if (TupleSetName == null && (mpObj.TupleSetName == null || mpObj.TupleSetName == string.Empty)) { return(contextCompare); } else if (TupleSetName == null && mpObj.TupleSetName != null) { //tupleSetNames not equal, so don't return 0 return(-1); } else { if (TupleSetName.CompareTo(mpObj.TupleSetName) == 0) { /* BUG 1064 - we have 2 markups for the same tuple child in the same * tuple set, so compare their markup data. If the data is the same * in both markups then consider them equal, otherwise they are * different markups (multiple occurrences of the same child). */ return(markupData.CompareTo(mpObj.markupData)); } else { //tupleSetNames don't match return(TupleSetName.CompareTo(mpObj.TupleSetName)); } } } else { return(unitCompare); } } else { return(contextCompare); } } return(nameCompare); }
/// <summary> /// Checks the following conditions: /// contextID == newContextID with the same internal data /// contextID == newContextID with different internal data /// contextID != newContextID with the same internal data /// contextID != newContextID with different internal data /// /// It changes the MarkupProperty accordingly /// </summary> /// <param name="mp">The markup property to be verified.</param> protected void CheckDuplicateContexts( MarkupProperty mp ) { ContextProperty cp = mp.contextRef; foreach ( ContextProperty existingCP in contexts ) { // context ids are the same if ( string.Compare( existingCP.ContextID, cp.ContextID ) == 0 ) { // duplicate id found if ( existingCP.ValueEquals( cp ) == false ) { // but contains unique data, just give it a new id cp.ContextID = CreateUniqueName( cp.ContextID, contexts ); // now rerun the check to see if data is duplicated CheckDuplicateContexts( mp ); break; } else { // they are complete duplicates - they won't be written out twice } } else // names aren't the same, is the data the same? { if ( existingCP.ValueEquals( cp ) == true ) { // point to the context that already exists mp.contextRef = existingCP; break; } } } }
internal static bool TryCreateFromInlineXbrl(XmlNode data, string inLineXBRLPrefix, string xbrliPrefix, Dictionary <string, string> namespaceused, ArrayList contexts, ArrayList units, bool isNumeric, Dictionary <string, Dictionary <decimal, ITupleSetChild> > tupleMarkups, out MarkupProperty mp, ref ArrayList errors) { foreach (XmlAttribute attr in data.Attributes) { if (attr.Value.ToLower().Equals(DocumentBase.INLINE_XBRL_URI.ToLower())) { inLineXBRLPrefix = attr.LocalName; } else if (attr.Value.ToLower().Equals(DocumentBase.XBRL_INSTANCE_URL.ToLower())) { xbrliPrefix = attr.LocalName; } } mp = new MarkupProperty(); if (data.Attributes[inLineXBRLPrefix + ":order"] != null && data.Attributes[inLineXBRLPrefix + ":tupleRef"] == null) { //these are tuple children that are nested inside tuples... //better evaluate it with the tuple..... return(false); } /* * <ix:nonFraction ix:name="us-gaap:SellingAndMarketingExpense" * ix:format="commadot" ix:scale="6" decimals="-6" unitRef="usd" * contextRef="c20080331_9_cik_00009869________">9,161 * </ix:nonFraction> */ XmlAttribute nameAttr = data.Attributes[inLineXBRLPrefix + ":name"]; if (nameAttr == null) { nameAttr = data.Attributes["name"]; } if (nameAttr == null) { errors.Add("Failed to parse " + data.OuterXml); return(false); } // first get the name string[] nameParts = nameAttr.Value.Split(':'); //TODO: need to parse this information //mp.elementNamespace = data.NamespaceURI; mp.elementPrefix = nameParts[0]; mp.elementName = nameParts[1]; mp.elementId = string.Format(DocumentBase.ID_FORMAT, nameParts[0], nameParts[1]); namespaceused.TryGetValue(mp.elementPrefix, out mp.elementNamespace); mp.element = new Node(new Element(mp.elementName, mp.elementId, false)); mp.element.MyElement.HasCompleteTupleFamily = false; // try and get the context ref XmlAttribute contextAttr = data.Attributes[Instance.CONTEXT_REF]; if (contextAttr == null) { contextAttr = data.Attributes[inLineXBRLPrefix + ":" + Instance.CONTEXT_REF]; } if (contextAttr == null) { Common.WriteError("XBRLParser.Error.ElementMissingRequiredField", errors, Instance.CONTEXT_REF); return(false); } string contextId = contextAttr.Value; // make sure we can find the real context int contextIndex = contexts.BinarySearch(contextId); if (contextIndex < 0) { Common.WriteError("XBRLParser.Error.ContextIdNotFound", errors, contextId); return(false); } mp.SetContext((ContextProperty)contexts[contextIndex]); if (isNumeric) { // and get the unit XmlAttribute unitAttr = data.Attributes[Instance.UNIT_REF]; if (unitAttr == null) { unitAttr = data.Attributes[inLineXBRLPrefix + ":" + Instance.UNIT_REF]; } if (unitAttr != null) { string unitId = unitAttr.Value; // make sure we can find the real context int unitIndex = units.BinarySearch(unitId); if (unitIndex < 0) { Common.WriteError("XBRLParser.Error.UnitIdNotFound", errors, unitId); return(false); } mp.SetUnit((UnitProperty)units[unitIndex]); // and precision XmlAttribute attr = data.Attributes[Precision.DECIMALS]; if (attr == null) { attr = data.Attributes[xbrliPrefix + ":" + Precision.DECIMALS]; } if (attr == null) { attr = data.Attributes[Precision.PRECISION]; } if (attr == null) { attr = data.Attributes[xbrliPrefix + ":" + Precision.PRECISION]; } if (attr != null) { Precision p = null; if (!Precision.TryCreateFromXml(attr, out p, ref errors)) { return(false); } mp.SetPrecision(p); } } } mp.markupData = data.InnerText; string formatvalue = string.Empty; XmlAttribute formatAttr = data.Attributes[inLineXBRLPrefix + ":format"]; if (formatAttr != null) { formatvalue = formatAttr.Value; string[] split = formatvalue.Split(':'); if (split.Length > 1) { formatvalue = split[1]; } } if (isNumeric) { int scaleVal = 0; bool isNegated = false; XmlAttribute scaleAttr = data.Attributes[inLineXBRLPrefix + ":scale"]; if (scaleAttr != null) { int.TryParse(scaleAttr.Value, out scaleVal); } if (data.Attributes[inLineXBRLPrefix + ":sign"] != null) { isNegated = true; } if (!TryConvertInlineXbrlNonFraction(scaleVal, isNegated, formatvalue, ref mp.markupData)) { errors.Add("Failed to parse element" + data.OuterXml); return(false); } } if (data.Attributes[inLineXBRLPrefix + ":order"] != null && data.Attributes[inLineXBRLPrefix + ":tupleRef"] != null) { decimal order = 0; if (decimal.TryParse(data.Attributes[inLineXBRLPrefix + ":order"].Value, out order)) { string tupleId = data.Attributes[inLineXBRLPrefix + ":tupleRef"].Value; Dictionary <decimal, ITupleSetChild> inner; if (!tupleMarkups.TryGetValue(tupleId, out inner)) { inner = new Dictionary <decimal, ITupleSetChild>(); tupleMarkups[tupleId] = inner; } inner[order] = mp; } else { errors.Add("Failed to parse ix:order information for element" + data.OuterXml); return(false); } } return(true); }
/// <summary> /// Sets and counts the different unit types used for an element based on the markup (<paramref name="mp"/>). /// </summary> /// <param name="predominantElementTypes">Holds the count of element uses for a given unit type.</param> /// <param name="mp">An instance of <see cref="MarkupProperty"/> which represents a single fact in the instance document.</param> private void CountUnitType( Dictionary<string, Dictionary<UnitType, int>> predominantElementTypes, MarkupProperty mp ) { UnitType unitType = GetUnitType( mp ); if( !predominantElementTypes.ContainsKey( mp.elementId ) ) predominantElementTypes[ mp.elementId ] = new Dictionary<UnitType, int>(); if( !predominantElementTypes[ mp.elementId ].ContainsKey( unitType ) ) predominantElementTypes[ mp.elementId ][ unitType ] = 0; predominantElementTypes[ mp.elementId ][ unitType ]++; }
/// <summary> /// Looks up the predefined <see cref="UnitType"/> or custom UnitType (bit value > 128) which is being used on the <paramref name="mp"/> (<see cref="MarkupProperty"/>). /// </summary> /// <param name="mp"></param> /// <returns></returns> private UnitType GetUnitType( MarkupProperty mp ) { if( InstanceReportColumn.IsEPSUnit( mp.unitRef ) ) return UnitType.EPS; if( InstanceReportColumn.IsExchangeRateUnit( mp.unitRef ) ) return UnitType.ExchangeRate; if( InstanceReportColumn.IsMonetaryUnit( mp.unitRef ) ) return UnitType.Monetary; if( InstanceReportColumn.IsSharesUnit( mp.unitRef ) ) return UnitType.Shares; if( InstanceReportColumn.IsCustomUnit( mp.unitRef ) ) { int pos = -1; string unit = mp.unitRef.StandardMeasure.MeasureValue; if( this.customUnits.ContainsKey( unit ) ) { pos = this.customUnits[ unit ]; } else { pos = 1 << 8; pos = pos << this.customUnits.Count; this.customUnits[ unit ] = pos; this.unitDictionary[ pos ] = unit; } return (UnitType)pos; } return UnitType.Other; }
private void AddFootnoteLocator(MarkupProperty mp, XmlElement parentElement) { XmlElement locator = null; locator = xDoc.CreateElement(DocumentBase.XBRL_LINKBASE_PREFIX, LOC, DocumentBase.XBRL_LINKBASE_URL); XmlAttribute typeAttr = xDoc.CreateAttribute( DocumentBase.XLINK_PREFIX, TYPE, DocumentBase.XLINK_URI ); typeAttr.Value = LOCATOR; locator.SetAttributeNode( typeAttr ); XmlAttribute hrefAttr = xDoc.CreateAttribute( DocumentBase.XLINK_PREFIX, HREF, DocumentBase.XLINK_URI ); hrefAttr.Value = string.Format( HREF_FORMAT, string.Empty, mp.Id ); locator.SetAttributeNode( hrefAttr ); XmlAttribute labelAttr = xDoc.CreateAttribute( DocumentBase.XLINK_PREFIX, LABEL, DocumentBase.XLINK_URI ); labelAttr.Value = string.Format( LABEL_FORMAT, mp.Id ); locator.SetAttributeNode( labelAttr ); parentElement.AppendChild(locator); if (writeComments && mp.xmlElement != null) { XmlComment locComment = xDoc.CreateComment( TraceUtility.FormatStringResource( "XBRLParser.Info.InstanceDocFootnoteMarkupAddress", mp.address, mp.xmlElement.Name ) ); parentElement.InsertBefore(locComment, locator); } }