Where Did The Segment Property Go?

The following email came into the Gepsio email inbox not long ago:

I upgraded to your latest release btw but it looks like the Segment object was removed. How do I access Dimensions and Segments now?
Thanks

The user had been using the Sep 2013 CTP [version 2.1.0.7] and had been using it to write code like this:

// this item has a dimension (grouped) 
if (item.ContextRef.Segment != null)
{
    foreach (System.Xml.XmlNode node in item.ContextRef.Segment)
    {
        //Insert_Dimension(FactItemID, node.Attributes["dimension"].Value, node.InnerText);
        ddi.NewRow(FactItemID, node.Attributes["dimension"].Value, node.InnerText);
    }
}

The short answer is that – oops! – the Segment property was moved from public visibility to internal visibility in the Nov 2014 CTP [2.1.0.8], and that change was retained in the May 2015 CTP [2.1.0.9]. It will be moved back to public visibility in the next CTP, but an explanation for the change is in order.

The Segment Property in the Sep 2013 CTP

In the Sep 2013 CTP, the public Segment property was a property of the Context class. It was of type System.Xml.XmlNode and was documented as follows:

The segment node defined for this context. If this context was not marked up with a segment node, then this property will return null.

This property was made available in support of section 4.7.3.2 of the XBRL 2.1 specification, which describes an optional <segment> child element of a <context> element:

The  <segment>  element is an optional container for additional mark-up that the preparer of an XBRL Instance SHOULD use to identify the business segment more completely in cases where the Entity identifier is insufficient. In general, the content of a  <segment>  will be specific to the purpose of the XBRL instance. Elements contained by the  <segment>  element MUST NOT be defined in the http://www.xbrl.org/2003/instance namespace. Also, they MUST NOT be in the substitution group for elements defined in the http://www.xbrl.org/2003/instance namespace. The  <segment>  element MUST NOT be empty.

The XBRL 2.1 specification does not associate any semantics with the <segment> element; therefore, Gepsio could not define an object model around this element, since it is documented as an optional container containing zero or more elements whose semantics were presumably documented elsewhere. Therefore, Gepsio could only expose the element, if it existed, as a generic XmlNode.

Introducing the Interface-Based XML Layer

On 18 July 2014, a changeset was checked in which changed Gepsio’s relationship to the .NET 3.5 XML DOM classes. This change was first published in a build in the Nov 2014 CTP and continued on in the May 2015 CTP. This change removed Gepsio’s direct dependency on the .NET 3.5 XML DOM classes and, instead, moved the support behind a set of internally-defined interfaces. The change introduced interfaces to handle all of the XML work, as well as classes that implemented the interface using the .NET 3.5 XML DOM classes. So, with this design, instead of Gepsio calling something like XmlDocument.Load() to load an XBRL document instance, it called an interface method called IDocument.Load(). The IDocument.Load() interface implementation called XmlDocument.Load() in its implementation.

The rationale behind moving Gepsio’s XML layer behind an interface was to make it easier to support other XML implementations in the future. The future of Gepsio, and its possible reach beyond purely .NET and possibly into other areas, whether it be as a Portable Class Library, as a library that would support Xamarin, or as a library that would support the Universal Windows Platform, means that Gepsio may need to be modified at its lowest level XML layer. For example, The Portable Class Library does not support the .NET 3.5 XML DOM classes. Instead, it supports only the LINQ-to-XML classes. Once XML is loaded, Gepsio is pretty portable, but the XML layer could potentially be a porting problem down the road should the XML runtime need to change.

Moving all of Gepsio’s low level XML needs behind an interface, and implementing the interface in a set of classes that Gepsio would actually be using, makes it easier to change the XML layer later on, should the need arise, without disturbing the rest of the Gepsio code base. Moving, for example, from the .NET 3.5 XML DOM classes to LINQ-to-XML would be a (theoretically) simple matter of implementing the internal XML interfaces in classes that used LINQ-to-XML. Gepsio’s higher levels of code – the XBRL semantics – would not need to change.

Interfaces and the Segment Property

The issue with the interface implementation with regards to the Segment property is that things like System.Xml.XmlNode, which were originally exposed as the type of the Segment property, are now behind an interface. With the XML interface design, Gepsio no longer sees a System.Xml.XmlNode object directly. Instead, it sees an internally-defined INode interface, and System.Xml.XmlNode is hidden behind the implementation of INode. With the interface design change, the Segment property in question would need to have its type changed from System.Xml.XmlNode to INode. The problem there is that INode is defined internally by Gepsio, and external callers, such as the code shown above, have no knowledge of INode. Failing that understanding, the Segment property could not be exposed, so the property was moved to be private in the Nov 2014 CTP.

The Road Forward

Since folks are, in fact, using the Segment property for Dimensions work (and, failing Gepsio’s first-party support for Dimensions, which needs to get on the project roadmap), then the Segment property will once again need to go public. Work will, therefore, progress in getting the XML interfaces documented so that people can use an internally-defined INode implementation to do work with segment nodes. This work is underway now.

Advertisements

Instance Documents, Schemas, and Linkbase Documents

A Gepsio user writes:

So reading through your blog and the discussions, I’ve come to realize one big thing that I’ve probably been missing so far — the other documents (Calculations, Schema, Definitions, Label, Presentation) that are listed on the EDGAR site are valuable to the comprehension of the XBRL document, correct? Is there some tutorial on how to load all of the documents into one session and use the corresponding references between them to produce a more robust result?

The short answer is that Gepsio detects and reads all of those ancillary files (called linkbase documents) for you, without you having to do anything but load the XBRL instance. Gepsio does the rest.

Let’s take some time and explore the longer answer. First, we will take a look at how XBRL defines the relationship between instance documents, schemas, and linkbase documents. That will be followed up with a look at how Gepsio uses and exposes the data from the various linkbase documents.

XBRL Relationship Between Instance Documents, Schemas, and Linkbase Documents

An XBRL document instance contains a reference to a schema document:

<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:dei="http://xbrl.us/dei/2009-01-31"
    xmlns:xbrli="http://www.xbrl.org/2003/instance"
    xmlns:fds="http://www.factset.com/2010-11-30"
    xmlns:iso4217="http://www.xbrl.org/2003/iso4217"
    xmlns:us-gaap="http://xbrl.us/us-gaap/2009-01-31"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>
  <link:schemaRef xlink:type="simple" xlink:href="fds-20101130.xsd" />

The schema, in turn, contains references the various linkbase documents:

<schema
  xmlns="http://www.w3.org/2001/XMLSchema"
  xmlns:xbrli="http://www.xbrl.org/2003/instance"
  xmlns:link="http://www.xbrl.org/2003/linkbase"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  targetNamespace="http://www.factset.com/2010-11-30"
  xmlns:fds="http://www.factset.com/2010-11-30"
  elementFormDefault="qualified"
  attributeFormDefault="unqualified"
  xmlns:us-types="http://xbrl.us/us-types/2009-01-31"
>
  <annotation>
    <appinfo>
      <!-- snip -->
      <link:linkbaseRef
          xlink:type="simple"
          xlink:href="fds-20101130_pre.xml"
          xlink:role="http://www.xbrl.org/2003/role/presentationLinkbaseRef"
          xlink:arcrole="http://www.w3.org/1999/xlink/properties/linkbase"
          xlink:title="Presentation Links, all"
      />
      <link:linkbaseRef
          xlink:type="simple"
          xlink:href="fds-20101130_cal.xml"
          xlink:role="http://www.xbrl.org/2003/role/calculationLinkbaseRef"
          xlink:arcrole="http://www.w3.org/1999/xlink/properties/linkbase"
          xlink:title="Calculation Links, all" 
      />
      <link:linkbaseRef
          xlink:type="simple"
          xlink:href="fds-20101130_lab.xml"
          xlink:role="http://www.xbrl.org/2003/role/labelLinkbaseRef"
          xlink:arcrole="http://www.w3.org/1999/xlink/properties/linkbase"
          xlink:title="Label Links, all"
      />
    </appinfo>
  </annotation>

The XBRL instance document is the “starting point” of this chain. The XBRL instance document references the schema, and the schema references the linkbase documents.

Gepsio Loading Instance Documents, Schemas, and Linkbase Documents

Because the XBRL instance document is the “starting point” of the document relationship chain, Gepsio can figure out all of the references when the XBRL instance document is loaded. Gepsio users load an XBRL instance document with the following code:

var MyXbrlDoc = new XbrlDocument();
MyXbrlDoc.Load("MyXbrlDoc.xml");

Gepsio does all of the work to load any referenced schemas and linkbase documents during the loading process automatically. (This is part of the appeal of Gepsio: freeing developers from having to chase down all of these references manually.)

Gepsio Exposing Data from Instance Documents and Schemas

Gepsio’s automatic loading of referenced schemas and linkbase documents is fine, but the real key is to be able to see the loaded reference data.

Referenced schemas are available as a collection in the loaded document’s fragments:

var MyXbrlDoc = new XbrlDocument();
MyXbrlDoc.Load("MyXbrlDoc.xml");
foreach(var currentFragment in MyXbrlDoc.XbrlFragments)
{
    foreach(var currentSchema in currentFragment.Schemas)
    {
        // schema information is available in the
        // "currentSchema" object
    }
}

Once the schema is available, information from the various linkbase documents is available in various collections found on the schema object.

Gepsio Exposing Data from Calculation Linkbases

Information from referenced calculation linkbases are available as a collection in the schema:

var MyXbrlDoc = new XbrlDocument();
MyXbrlDoc.Load("MyXbrlDoc.xml");
foreach(var currentFragment in MyXbrlDoc.XbrlFragments)
{
    foreach(var currentSchema in currentFragment.Schemas)
    {
        var currentCalculationLinkbase = currentSchema.CalculationLinkbase;
        if(currentCalculationLinkbase != null)
        {
            foreach(var currentCalculationLink in currentCalculationLinkbase.CalculationLinks)
            {
                // calculation link information is available in the
                // "currentCalculationLink" object
            }
        }
    }
}

Each calculation link in the calculation linkbase document is available from this collection.

Gepsio Exposing Data from Definition Linkbases

Information from referenced definition linkbases are available as a collection in the schema:

var MyXbrlDoc = new XbrlDocument();
MyXbrlDoc.Load("MyXbrlDoc.xml");
foreach(var currentFragment in MyXbrlDoc.XbrlFragments)
{
    foreach(var currentSchema in currentFragment.Schemas)
    {
        var currentDefinitionLinkbase = currentSchema.DefinitionLinkbase;
        if(currentDefinitionLinkbase != null)
        {
            foreach(var currentDefinitionLink in currentDefinitionLinkbase.DefinitionLinks)
            {
                // definition link information is available in the
                // "currentDefinitionLink" object
            }
        }
    }
}

Each definition link in the definition linkbase document is available from this collection.

Gepsio Exposing Data from Label Linkbases

Information from referenced label linkbases are available as a collection in the schema:

var MyXbrlDoc = new XbrlDocument();
MyXbrlDoc.Load("MyXbrlDoc.xml");
foreach(var currentFragment in MyXbrlDoc.XbrlFragments)
{
    foreach(var currentSchema in currentFragment.Schemas)
    {
        var currentLabelLinkbase = currentSchema.LabelLinkbase;
        if(currentLabelLinkbase != null)
        {
            foreach(var currentLabelLink in currentLabelLinkbase.LabelLinks)
            {
                // label link information is available in the
                // "currentLabelLink" object
            }
        }
    }
}

Each label link in the label linkbase document is available from this collection.

Gepsio Exposing Data from Presentation Linkbases

Information from referenced presentation linkbases are available as a collection in the schema:

var MyXbrlDoc = new XbrlDocument();
MyXbrlDoc.Load("MyXbrlDoc.xml");
foreach(var currentFragment in MyXbrlDoc.XbrlFragments)
{
    foreach(var currentSchema in currentFragment.Schemas)
    {
        var currentPresentationLinkbase = currentSchema.PresentationLinkbase;
        if(currentPresentationLinkbase != null)
        {
            foreach(var currentPresentationLink in currentLabelLinkbase.PresentationLinks)
            {
                // presentation link information is available in the
                // "currentPresentationLink" object
            }
        }
    }
}

Each presentation link in the presentation linkbase document is available from this collection.

Schema Errors Cause XBRL Facts To Not Be Created

A Gepsio user recently emailed in with an interesting problem: the XBRL document being loaded contained an dei:EntityRegistrantName element, but Gepsio did not have a fact with that name in the Facts collection of the XBRL fragment. What was going on?

The XBRL instance looked good. It defined the dei namespace and pulled in a schema:

<XBRL
    xmlns="http://www.xbrl.org/2003/instance"
    xmlns:us-gaap="http://fasb.org/us-gaap/2014-01-31"
    xmlns:xl="http://www.xbrl.org/2003/XLink"
    xmlns:us-types="http://fasb.org/us-types/2014-01-31"
    xmlns:iso4217="http://www.xbrl.org/2003/iso4217"
    xmlns:currency="http://xbrl.sec.gov/currency/2014-01-31"
    xmlns:country="http://xbrl.sec.gov/country/2013-01-31"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:xbrli="http://www.xbrl.org/2003/instance"
    xmlns:xbrldi="http://xbrl.org/2006/xbrldi"
    xmlns:dei="http://xbrl.sec.gov/dei/2014-01-31"
    xmlns:link="http://www.xbrl.org/2003/linkbase"
    xmlns:utr="http://www.xbrl.org/2009/utr"
    xmlns:compsci="http://compsciresources.com"
    xmlns:fds="http://investor.factset.com/20140831"
    xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
>
    <link:schemaRef xlink:type="simple" xlink:href="fds-20140831.xsd"/>

    <!-- snip -->
    <dei:EntityRegistrantName contextRef="c0_From1Sep2013To31Aug2014">
        FACTSET RESEARCH SYSTEMS INC
    </dei:EntityRegistrantName>

The problem was that the referenced schema never actually imported a schema that defined the DEI namespace:

<xs:schema
    xmlns:xbrli="http://www.xbrl.org/2003/instance"
    xmlns:num="http://www.xbrl.org/dtr/type/numeric"
    targetNamespace="http://investor.factset.com/20140831"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:nonnum="http://www.xbrl.org/dtr/type/non-numeric"
    xmlns:us-types="http://fasb.org/us-types/2014-01-31"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:link="http://www.xbrl.org/2003/linkbase"
    xmlns:fds="http://investor.factset.com/20140831"
    elementFormDefault="qualified"
    xmlns:xbrldt="http://xbrl.org/2005/xbrldt"
>
<!-- snip -->
<xs:import
    namespace="http://www.xbrl.org/2003/instance"
    schemaLocation="http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd"
/>
<xs:import
    namespace="http://www.xbrl.org/2003/linkbase"
    schemaLocation="http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd"
/>
<xs:import
    namespace="http://fasb.org/us-types/2014-01-31"
    schemaLocation="http://xbrl.fasb.org/us-gaap/2014/elts/us-types-2014-01-31.xsd"
/>
<xs:import
    namespace="http://www.xbrl.org/dtr/type/numeric"
    schemaLocation="http://www.xbrl.org/dtr/type/numeric-2009-12-16.xsd"
/>
<xs:import
    namespace="http://www.xbrl.org/dtr/type/non-numeric"
    schemaLocation="http://www.xbrl.org/dtr/type/nonNumeric-2009-12-16.xsd"
/>
<xs:import
    namespace="http://xbrl.org/2005/xbrldt"
    schemaLocation="http://www.xbrl.org/2005/xbrldt-2005.xsd"
/>

<!-- where is the import for http://xbrl.sec.gov/dei/2014-01-31? -->

When Gepsio started building facts for this instance, it found the dei:EntityRegistrantName element and attempted to build a Fact object. When Gepsio creates a Fact object, it begins by searching through the schemas, looking for the element definition. In this case, Gepsio could not find a schema element definition for dei:EntityRegistrantName, so it didn’t build a Fact object and didn’t put a fact in the Facts collection for dei:EntityRegistantName.

Gepsio did the right thing — it can’t build a Fact object if there is no definition for the element — but it could have done more to report the issue. Today, Gepsio is silent regarding uncreatable Fact objects. Perhaps a new validation error is needed to report this condition. Hopefully, a future CTP can address this need.

On Sunday 23 Mar 2014, Mike Willis posted a link to an article in the XBRL International group on Facebook. The article, with its intriguing title of “Why Investors Are Not Using XBRL“, argues that a data error in a 2012 10-Q filing from Applied Micro Circuits contained an error in its reporting of Warrants Compensation Expense and that, because of this error, the filing (and, by association, XBRL itself) cannot be used reliably.

(The filing referenced by the article is available from EDGAR here, and the raw XBRL data for this filing is available here.)

When I first read about the error, I considered two possibilities:

  1. The Warrants Compensation Expense fact was eligible to be a part of a calculation link, but was not authored as such. If this was the case, then the error was in the XBRL document’s creation process, because adding this item to a calculation link would have ensured that the calculation would be validated by XBRL validators like Gepsio. The calculation would have been flagged as an error by XBRL validators, which would have alerted validators to the error in the fact.
  2. The Warrants Compensation Expense fact was not eligible to be a part of a calculation link. If this was the case, then the fact did not contribute to a summation value and no calculation link would have been possible. In this case, then XBRL validators would have been unable to find the error in the value, since there would be no calculation link to validate.

I decided to use Gepsio to get some background on the issue. I was curious to see whether the article had pointed out an error with the filing, or if some other issue was involved with the filing.

The Investigation

I dug around in the filing’s XBRL instance and found the value in question:

<amcc:WarrantsCompensationExpense contextRef="ThreeMonthsEnded_30Jun2012" decimals="-5" unitRef="USD">1289000000</amcc:WarrantsCompensationExpense>

Then I dug around in the filing’s calculation linkbase document to see if the fact was referenced anywhere in a calculation link, and found this:

<calculationLink xlink:title="0130 - Statement - Condensed Consolidated Statements of Cash Flows (Unaudited)" xlink:role="http://www.appliedmicro.com/2011-06-30/role/StatementsOfCashFlows" xlink:type="extended">
    <!-- OTHER CALCULATION LOCATORS AND ARCS REMOVED FOR BREVITY -->

    <loc xlink:href="amcc-20120630.xsd#amcc_WarrantsCompensationExpense" xlink:type="locator" xlink:label="amcc_WarrantsCompensationExpense"/>
    <calculationArc xlink:type="arc" priority="1" weight="1" use="optional" order="65" xlink:to="amcc_WarrantsCompensationExpense" xlink:from="us-gaap_NetCashProvidedByUsedInOperatingActivities" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item"/>
</calculationLink>

Ah ha! So, the fact is involved in a calculation of some sort.

The next step was to see if Gepsio considered the XBRL document to be valid or invalid, perhaps because of a broken calculation link driven by the bad value of the Warrants Compensation Expense fact. I downloaded all of the filing’s XBRL data to my local machine and wrote a small Powershell script to process the filing through Gepsio’s Sep 2013 CTP:

Add-Type -Path "C:\Users\Jeff\Desktop\amcc-20120630\JeffFerguson.Gepsio.dll"
$XbrlDoc = New-Object -TypeName JeffFerguson.Gepsio.XbrlDocument
$DocumentLocation = "C:\Users\Jeff\Desktop\amcc-20120630\amcc-20120630.xml"
Clear
Write-Host "+---------------------------------+"
Write-Host "| LOADING AND VALIDATING DOCUMENT |"
Write-Host "+---------------------------------+"
Write-Host "Loading and validating" $DocumentLocation"..."
$XbrlDoc.Load($DocumentLocation)
Write-Host "+-------------------------------+"
Write-Host "| DOCUMENT LOADED AND VALIDATED |"
Write-Host "+-------------------------------+"
Write-Host "Loaded." $XbrlDoc.XbrlFragments.Count "fragments in document."
Write-Host "Is this document valid?" $XbrlDoc.IsValid
foreach($XbrlFragment in $XbrlDoc.XbrlFragments)
{
    Write-Host "+---------------+"
    Write-Host "| XBRL FRAGMENT |"
    Write-Host "+---------------+"
    Write-Host "Is this fragment valid?" $XbrlFragment.IsValid
    Write-Host $XbrlFragment.Facts.Count "facts in fragment."
    Write-Host $XbrlFragment.Units.Count "units in fragment."
    Write-Host $XbrlFragment.Contexts.Count "contexts in fragment."
}

The Powershell script gave me the following output:

+---------------------------------+
| LOADING AND VALIDATING DOCUMENT |
+---------------------------------+
Loading and validating C:\Users\Jeff\Desktop\amcc-20120630\amcc-20120630.xml...
+-------------------------------+
| DOCUMENT LOADED AND VALIDATED |
+-------------------------------+
Loaded. 1 fragments in document.
Is this document valid? True
+---------------+
| XBRL FRAGMENT |
+---------------+
Is this fragment valid? True
748 facts in fragment.
9 units in fragment.
180 contexts in fragment.

According to Gepsio, the filing is valid. Since Gepsio processes and validates calculation links, there are two possibilities:

  1. The document is indeed valid. If this is the case, then, by association, it follows that all of the calculation links are valid. This would imply that the calculation link involving the Warrants Compensation Expense fact is valid, and the value is not in error after all.
  2. The document is being incorrectly reported as valid. In this case, the document is invalid, perhaps due to the broken calculation link driven by the bad value of the Warrants Compensation Expense fact, and Gepsio has some sort of bug which is not finding, or correctly reporting, the error.

A Short Primer on Calculation Link Validation with Gepsio

Gepsio, as a part of its XBRL validation processing, examines all facts involved in calculation links and performs the math to ensure that values stored in facts reflect numeric data that reflects the mathematics specified by the link. If Gepsio finds an error in the math behind a calculation link, it creates a SummationConceptValidationError object, populates the object with all of the relevant information about the failed calculation validation, and stores the object in the ValidationErrors collection of the XbrlFragment object that contains the facts involved in the calculation.

 If Gepsio does not find an error in the math behind a calculation link, then no SummationConceptValidationError object is created, the variables created to validate the calculation link are discarded, and Gepsio moves onto validating the next calculation link. There is, in this case, no record of the validation.

The Case for Retaining Calculation Link Validation Results

In our example above, there are no SummationConceptValidationError objects in the XbrlFragment (indeed, there are no validation error objects of any kind in the fragment). But, since all of the calculations have passed Gepsio’s inspection, and the variables involved in the calculation links have been discarded, there is no way to “debug” Gepsio at runtime to ensure that the calculation links were validated correctly. There is a need to retain these calculation link validation variables so that Gepsio can be easily debugged to ensure that it is performing the calculation link validations correctly.

If these calculation link values were available for all calculation links, then the results could be examined and compared against the expected results to see if there is an error in Gepsio’s calculation link validation code.

The Path Forward

I will create classes for Gepsio that will allow all calculation link validation data to be retained. Objects of these classes will be made available to the caller once Gepsio completes its load and validation work. I have previously written about other work that I would like to do to enhance Gepsio’s support of calculation links, so it seems like this work can be done at the same time.

Once these changes are in place, I can rerun this XBRL instance against the Applied Micro Circuits 10-Q, look at the Gepsio calculation link math by enhancing the PowerShell script to output each of the calculation link variables, see if there are any Gepsio errors that need to be debugged, and hopefully move closer to getting answers regarding whether or not the original error could have been caught by an XBRL validator.

Searching for Balance Sheet Calculations with the Sep 2013 CTP

I have been working with a Gepsio user who is interested in finding balance sheet information for the Target Corporation 10-K posted at http://www.sec.gov/Archives/edgar/data/27419/000104746913003100/0001047469-13-003100-index.htm. The user wrote:

 “I am looking at the CosolidatedStatementOfFinancialPositionUnaudited, and I am trying to get all of the fields used in the _cal.xml file for Balance Sheet (or CosolidatedStatementOfFinancialPositionUnaudited).  This “role” has all of the fields used to calc the balance sheet, and calculationArc tells me what heading the field is under (i.e. AssetsCurrent, InventoryNet, StockholderEquity).  It also holds the names of the field tgt_AccumulatedOtherComprehensiveIncomeLossForeignCurrencyTranslationAdjustmentAndCashFlowHedgesNetOfTax that is not a standard GAAP field.”

The BalanceSheet role type is defined in the schema for the 10-K:

<link:roleType roleURI="http://www.target.com/role/BalanceSheet" id="BalanceSheet">
    <link:definition>0030 - Statement - Consolidated Statements of Financial Position</link:definition>
    <link:usedOn>link:presentationLink</link:usedOn>
    <link:usedOn>link:calculationLink</link:usedOn>
    <link:usedOn>link:definitionLink</link:usedOn>
</link:roleType>

The role has a URI of “http://www.target.com/role/BalanceSheet”, which is also referenced in the calculation link through the calculation link’s “role” attribute:

<link:calculationLink xlink:type="extended" xlink:role="http://www.target.com/role/BalanceSheet">
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_Assets" xlink:label="Assets" xlink:title="Assets" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_AssetsCurrent" xlink:label="AssetsCurrent" xlink:title="AssetsCurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="Assets" xlink:to="AssetsCurrent" xlink:title="calculation: Assets to AssetsCurrent" order="1.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_CashCashEquivalentsAndShortTermInvestments" xlink:label="CashCashEquivalentsAndShortTermInvestments" xlink:title="CashCashEquivalentsAndShortTermInvestments" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="AssetsCurrent" xlink:to="CashCashEquivalentsAndShortTermInvestments" xlink:title="calculation: AssetsCurrent to CashCashEquivalentsAndShortTermInvestments" order="1.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_LoansHeldForSaleConsumerCreditCard" xlink:label="LoansHeldForSaleConsumerCreditCard" xlink:title="LoansHeldForSaleConsumerCreditCard" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="AssetsCurrent" xlink:to="LoansHeldForSaleConsumerCreditCard" xlink:title="calculation: AssetsCurrent to LoansHeldForSaleConsumerCreditCard" use="optional" order="2.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_NotesReceivableNet" xlink:label="NotesReceivableNet" xlink:title="NotesReceivableNet" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="AssetsCurrent" xlink:to="NotesReceivableNet" xlink:title="calculation: AssetsCurrent to NotesReceivableNet" order="3.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_InventoryNet" xlink:label="InventoryNet" xlink:title="InventoryNet" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="AssetsCurrent" xlink:to="InventoryNet" xlink:title="calculation: AssetsCurrent to InventoryNet" order="4.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_OtherAssetsCurrent" xlink:label="OtherAssetsCurrent" xlink:title="OtherAssetsCurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="AssetsCurrent" xlink:to="OtherAssetsCurrent" xlink:title="calculation: AssetsCurrent to OtherAssetsCurrent" order="5.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_PropertyPlantAndEquipmentNet" xlink:label="PropertyPlantAndEquipmentNet" xlink:title="PropertyPlantAndEquipmentNet" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="Assets" xlink:to="PropertyPlantAndEquipmentNet" xlink:title="calculation: Assets to PropertyPlantAndEquipmentNet" order="2.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_Land" xlink:label="Land" xlink:title="Land" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="PropertyPlantAndEquipmentNet" xlink:to="Land" xlink:title="calculation: PropertyPlantAndEquipmentNet to Land" order="1.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_BuildingsAndImprovementsGross" xlink:label="BuildingsAndImprovementsGross" xlink:title="BuildingsAndImprovementsGross" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="PropertyPlantAndEquipmentNet" xlink:to="BuildingsAndImprovementsGross" xlink:title="calculation: PropertyPlantAndEquipmentNet to BuildingsAndImprovementsGross" order="2.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_FurnitureAndFixturesGross" xlink:label="FurnitureAndFixturesGross" xlink:title="FurnitureAndFixturesGross" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="PropertyPlantAndEquipmentNet" xlink:to="FurnitureAndFixturesGross" xlink:title="calculation: PropertyPlantAndEquipmentNet to FurnitureAndFixturesGross" order="3.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_CapitalizedComputerSoftwareGross" xlink:label="CapitalizedComputerSoftwareGross" xlink:title="CapitalizedComputerSoftwareGross" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="PropertyPlantAndEquipmentNet" xlink:to="CapitalizedComputerSoftwareGross" xlink:title="calculation: PropertyPlantAndEquipmentNet to CapitalizedComputerSoftwareGross" use="optional" order="4.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_ConstructionInProgressGross" xlink:label="ConstructionInProgressGross" xlink:title="ConstructionInProgressGross" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="PropertyPlantAndEquipmentNet" xlink:to="ConstructionInProgressGross" xlink:title="calculation: PropertyPlantAndEquipmentNet to ConstructionInProgressGross" order="5.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_AccumulatedDepreciationDepletionAndAmortizationPropertyPlantAndEquipment" xlink:label="AccumulatedDepreciationDepletionAndAmortizationPropertyPlantAndEquipment" xlink:title="AccumulatedDepreciationDepletionAndAmortizationPropertyPlantAndEquipment" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="PropertyPlantAndEquipmentNet" xlink:to="AccumulatedDepreciationDepletionAndAmortizationPropertyPlantAndEquipment" xlink:title="calculation: PropertyPlantAndEquipmentNet to AccumulatedDepreciationDepletionAndAmortizationPropertyPlantAndEquipment" order="6.0" weight="-1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_OtherAssetsNoncurrent" xlink:label="OtherAssetsNoncurrent" xlink:title="OtherAssetsNoncurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="Assets" xlink:to="OtherAssetsNoncurrent" xlink:title="calculation: Assets to OtherAssetsNoncurrent" order="3.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_LiabilitiesAndStockholdersEquity" xlink:label="LiabilitiesAndStockholdersEquity" xlink:title="LiabilitiesAndStockholdersEquity" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_LiabilitiesCurrent" xlink:label="LiabilitiesCurrent" xlink:title="LiabilitiesCurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesAndStockholdersEquity" xlink:to="LiabilitiesCurrent" xlink:title="calculation: LiabilitiesAndStockholdersEquity to LiabilitiesCurrent" order="1.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_AccountsPayableCurrent" xlink:label="AccountsPayableCurrent" xlink:title="AccountsPayableCurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesCurrent" xlink:to="AccountsPayableCurrent" xlink:title="calculation: LiabilitiesCurrent to AccountsPayableCurrent" order="1.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_AccruedLiabilitiesCurrent" xlink:label="AccruedLiabilitiesCurrent" xlink:title="AccruedLiabilitiesCurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesCurrent" xlink:to="AccruedLiabilitiesCurrent" xlink:title="calculation: LiabilitiesCurrent to AccruedLiabilitiesCurrent" order="2.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_OtherShortTermBorrowings" xlink:label="OtherShortTermBorrowings" xlink:title="OtherShortTermBorrowings" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesCurrent" xlink:to="OtherShortTermBorrowings" xlink:title="calculation: LiabilitiesCurrent to OtherShortTermBorrowings" order="3.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_SecuredDebtCurrent" xlink:label="SecuredDebtCurrent" xlink:title="SecuredDebtCurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesCurrent" xlink:to="SecuredDebtCurrent" xlink:title="calculation: LiabilitiesCurrent to SecuredDebtCurrent" use="optional" order="4.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_LiabilitiesNoncurrent" xlink:label="LiabilitiesNoncurrent" xlink:title="LiabilitiesNoncurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesAndStockholdersEquity" xlink:to="LiabilitiesNoncurrent" xlink:title="calculation: LiabilitiesAndStockholdersEquity to LiabilitiesNoncurrent" order="2.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_UnsecuredLongTermDebt" xlink:label="UnsecuredLongTermDebt" xlink:title="UnsecuredLongTermDebt" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesNoncurrent" xlink:to="UnsecuredLongTermDebt" xlink:title="calculation: LiabilitiesNoncurrent to UnsecuredLongTermDebt" order="1.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_SecuredDebtOther" xlink:label="SecuredDebtOther" xlink:title="SecuredDebtOther" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesNoncurrent" xlink:to="SecuredDebtOther" xlink:title="calculation: LiabilitiesNoncurrent to SecuredDebtOther" use="optional" order="2.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_DeferredTaxLiabilitiesNoncurrent" xlink:label="DeferredTaxLiabilitiesNoncurrent" xlink:title="DeferredTaxLiabilitiesNoncurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesNoncurrent" xlink:to="DeferredTaxLiabilitiesNoncurrent" xlink:title="calculation: LiabilitiesNoncurrent to DeferredTaxLiabilitiesNoncurrent" order="3.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_OtherLiabilitiesNoncurrent" xlink:label="OtherLiabilitiesNoncurrent" xlink:title="OtherLiabilitiesNoncurrent" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesNoncurrent" xlink:to="OtherLiabilitiesNoncurrent" xlink:title="calculation: LiabilitiesNoncurrent to OtherLiabilitiesNoncurrent" order="4.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_StockholdersEquity" xlink:label="StockholdersEquity" xlink:title="StockholdersEquity" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="LiabilitiesAndStockholdersEquity" xlink:to="StockholdersEquity" xlink:title="calculation: LiabilitiesAndStockholdersEquity to StockholdersEquity" order="3.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_CommonStockValue" xlink:label="CommonStockValue" xlink:title="CommonStockValue" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="StockholdersEquity" xlink:to="CommonStockValue" xlink:title="calculation: StockholdersEquity to CommonStockValue" use="optional" order="1.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_AdditionalPaidInCapitalCommonStock" xlink:label="AdditionalPaidInCapitalCommonStock" xlink:title="AdditionalPaidInCapitalCommonStock" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="StockholdersEquity" xlink:to="AdditionalPaidInCapitalCommonStock" xlink:title="calculation: StockholdersEquity to AdditionalPaidInCapitalCommonStock" use="optional" order="2.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_RetainedEarningsAccumulatedDeficit" xlink:label="RetainedEarningsAccumulatedDeficit" xlink:title="RetainedEarningsAccumulatedDeficit" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="StockholdersEquity" xlink:to="RetainedEarningsAccumulatedDeficit" xlink:title="calculation: StockholdersEquity to RetainedEarningsAccumulatedDeficit" use="optional" order="3.0" weight="1" />
  <link:loc xlink:type="locator" xlink:href="http://xbrl.fasb.org/us-gaap/2012/elts/us-gaap-2012-01-31.xsd#us-gaap_AccumulatedOtherComprehensiveIncomeLossDefinedBenefitPensionAndOtherPostretirementPlansNetOfTax" xlink:label="AccumulatedOtherComprehensiveIncomeLossDefinedBenefitPensionAndOtherPostretirementPlansNetOfTax" xlink:title="AccumulatedOtherComprehensiveIncomeLossDefinedBenefitPensionAndOtherPostretirementPlansNetOfTax" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="StockholdersEquity" xlink:to="AccumulatedOtherComprehensiveIncomeLossDefinedBenefitPensionAndOtherPostretirementPlansNetOfTax" xlink:title="calculation: StockholdersEquity to AccumulatedOtherComprehensiveIncomeLossDefinedBenefitPensionAndOtherPostretirementPlansNetOfTax" use="optional" order="4.0" weight="-1" />
  <link:loc xlink:type="locator" xlink:href="tgt-20130202.xsd#tgt_AccumulatedOtherComprehensiveIncomeLossForeignCurrencyTranslationAdjustmentAndCashFlowHedgesNetOfTax" xlink:label="AccumulatedOtherComprehensiveIncomeLossForeignCurrencyTranslationAdjustmentAndCashFlowHedgesNetOfTax" xlink:title="AccumulatedOtherComprehensiveIncomeLossForeignCurrencyTranslationAdjustmentAndCashFlowHedgesNetOfTax" />
  <link:calculationArc xlink:type="arc" xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" xlink:from="StockholdersEquity" xlink:to="AccumulatedOtherComprehensiveIncomeLossForeignCurrencyTranslationAdjustmentAndCashFlowHedgesNetOfTax" xlink:title="calculation: StockholdersEquity to AccumulatedOtherComprehensiveIncomeLossForeignCurrencyTranslationAdjustmentAndCashFlowHedgesNetOfTax" use="optional" order="5.0" weight="1" />
</link:calculationLink>

This data path should answer the user’s question from an XBRL perspective. How can all of this data be accessed by Gepsio? Two things need to happen here:

  1. Use Gepsio to find the correct role type
  2. Use Gepsio to find the calculation links

Finding Role Type Information Using Gepsio

Gepsio includes a class called RoleType, which defines all of the properties of a role type found in a schema. The RoleType class includes the following properties:

  • Definition: The definition of this role type.
  • Id: The ID of this role type.
  • RoleUri: The URI for this role.
  • Schema: The schema that references this role type.
  • UsedOnReferences: A collection of “UsedOn” references for this role type. Used to identify what elements may use a taxonomy defined role or arc role value.

In Gepsio, RoleType objects are available as a collection in a schema. Schemas are available as a collection in an XBRL fragment, and XBRL fragments are available as a collection in an XBRL document. Given that chain of objects, code can be written to find a role type object given its ID:

private RoleType GetRoleTypeById(XbrlDocument xbrlDoc, string roleTypeId)
{
    foreach(var currentFragment in xbrlDoc.XbrlFragments)
    {
        foreach(var currentSchema in currentFragment.Schemas)
        {
            foreach(var currentRoleType in currentSchema.RoleTypes)
            {
                if(currentRoleType.Id.Equals(roleTypeId) == true)
                {
                    return currentRoleType;
                }
            }
        }
    }
    return null;
}

Once that RoleType object is returned from the method, then its RoleUri property can be examined to find the role’s URI with something like this:

var xbrlDoc = new XbrlDocument();
xbrlDoc.Load(“http://www.sec.gov/Archives/edgar/data/27419/000104746913003100/tgt-20130202.xml”);
var balanceSheetRoleType = GetRoleTypeById(xbrlDoc, “BalanceSheet”);
var balanceSheetRoleUri = balanceSheetRoleType.RoleUri;

Now that we have the URI for the role, we should be able to use that URI to look through the loaded calculation links to find the links that use the same role URI.

Now that we know how to find role types, let’s find the calculation links.

Finding Calculation Linkbase Information Using Gepsio

Like role types, calculation links are available through the schemas which reference them. The XbrlSchema object’s LinkbaseDocuments property is a collection of LinkbaseDocument objects. Each LinkbaseDocument maintains a CalculationLinks property, which is a collection of CalculationLink objects.  Given that chain of objects, code can be written to find all of the calculation links for a document:

private void GetCalculationLinks(XbrlDocument xbrlDoc)
{
    foreach(var currentFragment in xbrlDoc.XbrlFragments)
    {
        foreach(var currentSchema in currentFragment.Schemas)
        {
            foreach(var currentLinkbaseDocument in currentSchema.LinkbaseDocuments)
            {
                foreach(var currentCalculationLink in currentLinkbaseDocument.CalculationLinks)
                {
                    // is this the calculation link we want?
                }
            }
        }
    }
}

The question now is: how do we know that the calculation link found is the one we want?

… And Here Comes The Problem …

At this point, code should be able to look at a calculation link loaded by Gepsio to see if its role URI matches the URI in the role type that was found by the GetRoleTypeById() code proposed above. Here is the problem, however: although although the XBRL <calculationLink> element includes an attribute called xlink:role to describe the link’s role URI, Gepsio does not expose that value as a property of the CalculationLink class. It would be ideal to write something like this:

foreach(var currentCalculationLink in currentLinkbaseDocument.CalculationLinks)
{
    if(currentCalculationLink.RoleUri.Equals(balanceSheetRoleType.RoleUri) == true)
    {
        // this is the calculation link we want!
    }
}

However, the problem is that Gepsio’s CalculationLink class does not implement a RoleUri property, so this code doesn’t work, because the role URI isn’t available for a calculation link.

The Way Forward

Fortunately, the lack of a RoleUri property on the CalculationLink class is the only thing missing from the original user’s perspective. Adding this property into Gepsio shouldn’t take long at all. Although I am currently involved in getting Gepsio to work on .NET 4.5, WinRT and Windows Phone 8 through the Portable Class Library technology, I can put that work aside for a bit and release a new CTP that exposes a RoleUri property for the calculation link class.

So, here is the current plan:

  1. Shelve my “Portable Gepsio” changes.
  2. Get back to the current Gepsio codebase (which is released as 2.1.0.7).
  3. Add support for the RoleUri property in the Gepsio CalculationLink class.
  4. Build and release a new Gepsio CTP as version 2.1.0.8.
  5. write a follow-up blog post describing how to find a calculation link based on a role type, which was the original question.
  6. Unshelve the “Portable Gepsio” changes and return to that work.

Wish me luck!

Importing XBRL Fact Values Into SQL Server Using PowerShell and Gepsio

A Gepsio user has just written in with a success story on importing XBRL data into a SQL Server database using SQL Server Integration Services (SSIS), PowerShell and Gepsio. The solution was too good not to share, and, with the user’s permission, this blog post will describe that solution.

The user was tasked with importing some XBRL data into a SQL Server database instance. The overall architectural idea was to use an SSIS package to run a PowerShell script which would create a comma separated values (CSV) file from a raw XBRL document, and the values in the CSV would be used to import data into the database.

The first trick, which isn’t really XBRL-specific but still worth mentioning, is to get SSIS to run a PowerShell script. Basically, PowerShell scripts are run from within an SSIS package by adding an “Execute Process Task” and using a command line in the following form:

C:\[PATHTOPOWERSHELL]\PowerShell.exe -ExecutionPolicy ByPass -command ". 'C:\SCRIPTPATH\MyScript.ps1' 'param1' 'param2'"

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Read the forum discussion at http://social.msdn.microsoft.com/Forums/en-NZ/sqlintegrationservices/thread/216d2ee6-0f04-480f-808d-8241bc4a8d18 for more information about this process.

The next trick, of course, is creating the CSV file from the raw XBRL document. The user turned to PowerShell and Gepsio for this work, and life became a lot easier. Here is the example PowerShell script that can do this work:

param([string]$instFile = "C:\XBRLDOCPATH\XbrlDocument.xml")

#load Gepsio
Add-Type -Path "C:\GEPSIOPATH\JeffFerguson.Gepsio.dll"
$XbrlDoc = New-Object -TypeName JeffFerguson.Gepsio.XbrlDocument
$XbrlDoc.Load($instFile)
$instCSV = "C:\OUTPUTPATH\Allinfo.csv"

New-Item $instCSV -type "file" -force -value "EntityRegName,EntityFilerCat,FactName,Value`r`n"
$stream = new-object system.IO.StreamWriter($instCSV,$true)

[string] $script:Entity =""
try
{
foreach($CurrentFragment in $XbrlDoc.XbrlFragments)
{
GetEntityInfo $Currentfragment
try
{
WriteItemCSV $CurrentFragment "EarningsPerShareBasic"
}
catch
{
Write-Error("us-gaap_EarningsPerShareBasic: " +$_ )
}
try
{
WriteItemCSV $CurrentFragment "NetIncomeLoss"
}
catch
{
Write-Error(":us-gaap_NetIncomeLoss " +$_ )
}
}
}
catch
{
Write-Error("main foreach writeloop: " +$_ )
}
finally
{
$stream.close()
$stream.Dispose()
}

Function GetEntityInfo
{
param($fragment)
$script:Entity = ""
$entr = $fragment.Facts | Where-Object {$_.Name -eq "EntityRegistrantName"}
if(!$entr)
{
$entr = ""
}
$efc = $fragment.Facts | Where-Object {$_.Name -eq "EntityFilerCategory"}
if(!$efc)
{
$efc = ""
}
$script:Entity = "`"" + $entr.Value + "`",`"" + $efc.Value + "`","
}

Function WriteItemCSV
{
param($fragment, $ElId)
$Ff = $fragment.Facts | Where-Object {$_.Name -eq $ElId}
if($Ff)
{
[string]$S = $script:Entity
if ($Ff.GetType().fullname -eq "JeffFerguson.Gepsio.Item")
{
[string]$S = $script:Entity
if( $Ff.Name)
{
$S = $S + "`"" + $Ff.Name + "`","
}
else
{
$S = $S + "" +","
}
if( $Ff.Value)
{
$S = $S + $Ff.Value + ","
}
else
{
$S = $S + "" +","
}
$stream.WriteLine($S)
}
if ($Ff.GetType().fullname -eq "System.Object[]")
{
foreach($i in $Ff)
{
[string]$S = $script:Entity
if( $i.Name)
{
$S = $S + "`"" + $i.Name + "`","
}
else
{
$S = $S + "" +","
}
if( $i.Value)
{
$S = $S + $i.Value
}
else
{
$S = $S + ""
}
$stream.WriteLine($S)
}
}
}
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Let’s take a look at this PowerShell script in more detail.

The opening statements adds the Gepsio types to the PowerShell script and loads the XBRL document named in a PowerShell script command line parameter into a new Gepsio XbrlDocument instance:

param([string]$instFile = "C:\XBRLDOCPATH\XbrlDocument.xml")

#load Gepsio
Add-Type -Path "C:\GEPSIOPATH\JeffFerguson.Gepsio.dll"
$XbrlDoc = New-Object -TypeName JeffFerguson.Gepsio.XbrlDocument
$XbrlDoc.Load($instFile)

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

At this point, the PowerShell script maintains a variable called $XbrlDoc which contains all of Gepsio’s knowledge about the loaded XBRL document. Loading and validating an XBRL document can’t get much easier.

Once the XBRL document is loaded, then the output CSV file is created:

New-Item $instCSV -type "file" -force -value "EntityRegName,EntityFilerCat,FactName,Value`r`n"    
$stream = new-object system.IO.StreamWriter($instCSV,$true)

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Here, a new file is created and the CSV column header row is written to the new file. The CSV is set up to capture the following values found in the XBRL document:

  • Entity Registrant Name
  • Entity Filer Category

A .NET StreamWriter object is created to reference the newly created CSV file and is available from within a PowerShell script variable called $stream.

Once the CSV is available, the PowerShell script iterates through each of the XBRL fragments found in the XBRL document loaded by Gepsio:

try
{
foreach($CurrentFragment in $XbrlDoc.XbrlFragments)
{
GetEntityInfo $Currentfragment
try
{
WriteItemCSV $CurrentFragment "EarningsPerShareBasic"
}
catch
{
Write-Error("us-gaap_EarningsPerShareBasic: " +$_ )
}
try
{
WriteItemCSV $CurrentFragment "NetIncomeLoss"
}
catch
{
Write-Error(":us-gaap_NetIncomeLoss " +$_ )
}
}
}
catch
{
Write-Error("main foreach writeloop: " +$_ )
}
finally
{
$stream.close()
$stream.Dispose()
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

For each XBRL fragment found in the Gepsio document instance, entity information is read and written into the CSV file. Entity information is read from the fragment, and then values are written to the CSV. These operations are performed by functions in the PowerShell script called GetEntityInfo and WriteItemCSV, respectively.

Let’s take a look at the GetEntityInfo function. It is defined as follows:

Function GetEntityInfo
{
param($fragment)
$script:Entity = ""
$entr = $fragment.Facts | Where-Object {$_.Name -eq "EntityRegistrantName"}
if(!$entr)
{
$entr = ""
}
$efc = $fragment.Facts | Where-Object {$_.Name -eq "EntityFilerCategory"}
if(!$efc)
{
$efc = ""
}
$script:Entity = "`"" + $entr.Value + "`",`"" + $efc.Value + "`","
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

This function, which accepts a Gepsio XbrlFragment object as a parameter, searches through each of the fragment’s facts, looking for a fact whose name is “EntityRegistrantName” or “EntityFilerCategory”. If they are found, the Gepsio Fact objects are stored in local script variables — $entr and $efc, respectively. Once the search is complete, the Fact object’s values are stored as a string in a global-level script variable called $script:Entity.

The other interesting function in the PowerShell script is the WriteItemCSV function:

Function WriteItemCSV
{
param($fragment, $ElId)
$Ff = $fragment.Facts | Where-Object {$_.Name -eq $ElId}
if($Ff)
{
[string]$S = $script:Entity
if ($Ff.GetType().fullname -eq "JeffFerguson.Gepsio.Item")
{
[string]$S = $script:Entity
if( $Ff.Name)
{
$S = $S + "`"" + $Ff.Name + "`","
}
else
{
$S = $S + "" +","
}
if( $Ff.Value)
{
$S = $S + $Ff.Value + ","
}
else
{
$S = $S + "" +","
}
$stream.WriteLine($S)
}
if ($Ff.GetType().fullname -eq "System.Object[]")
{
foreach($i in $Ff)
{
[string]$S = $script:Entity
if( $i.Name)
{
$S = $S + "`"" + $i.Name + "`","
}
else
{
$S = $S + "" +","
}
if( $i.Value)
{
$S = $S + $i.Value
}
else
{
$S = $S + ""
}
$stream.WriteLine($S)
}
}
}
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

This function accepts both an Gepsio XBRL Fragment object and a fact name as parameters. It begins by searching through each of the supplied fragment’s facts, looking for a fact whose name matches the supplied name. If it is found, the Gepsio Fact objects are stored in a local script variable called $Ff. If the fact is found, its name and values are appended to the global script variable $script:Entity, after which the entire line built up in the $script:Entity variable is written out to the CSV stream.

[One quick note about the placement of the functions in the PowerShell script: When the script is executed from the PowerShell command line, the functions can be found anywhere in the script. SSIS, however, may need to have the functions defined before they are used in the script. If SSIS does not execute the script as written, the functions may need to be moved forward in the script so that they are defined before they are used.]

In the end, the PowerShell script will produce a CSV file that looks something like this:

EntityRegName,EntityFilerCat,FactName,Value
"COCA COLA CO","Large Accelerated Filer","EarningsPerShareBasic",1.22
"COCA COLA CO","Large Accelerated Filer","EarningsPerShareBasic",2.05
"COCA COLA CO","Large Accelerated Filer","EarningsPerShareBasic",2.14
"COCA COLA CO","Large Accelerated Filer","EarningsPerShareBasic",1.24
"COCA COLA CO","Large Accelerated Filer","NetIncomeLoss",4703000000
"COCA COLA CO","Large Accelerated Filer","NetIncomeLoss",2788000000
"COCA COLA CO","Large Accelerated Filer","NetIncomeLoss",4842000000
"COCA COLA CO","Large Accelerated Filer","NetIncomeLoss",2800000000

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Once this script is in place, the SSIS package to actually import the data becomes an easy set of two simple tasks:

  1. execute PowerShell script to create CSV from XBRL
  2. import CSV data into a database table

For the sake of brevity, the PowerShell sample shown here loads only a few XBRL values into the CSV file. In practice, however, there is no limit to the amount of data that could be loaded into the CSV.

Once all of this was working, the user sent in a testimonial that read:

“Thank you for making Gepsio available.  It saved me an awful lot of work (xpath, YIKES)!”.

This is gratifying, as this is what Gepsio is all about: making access to XBRL data easier without resorting to complicated XPath queries or understanding a lot about the rules of XBRL validation or technical syntax. Gepsio provides access to XBRL data without needing to worry about XBRL itself, which frees developers to work on the more important problem of building added value on top of the data.

Many thanks go out to the Gepsio user who provided this solution.

Using Gepsio From C#

A developer new to Gepsio asked for a C#-based sample of using Gepsio. I have built a simple C# console app using Visual Studio 2010 and the Gepsio Nov 2011 CTP. I’ll show the code first, and then I will discuss some key points after the code is shown:

using JeffFerguson.Gepsio;
using System;

namespace GepsioConsole
{
class Program
{
/// <summary>
/// The magic begins here.
/// </summary>
/// <param name="args">
/// A collection of program arguments. One argument should be supplied: the address of the XBRL document
/// to load.
/// </param>
static void Main(string[] args)
{
if (args.Length != 1)
{
Console.WriteLine("usage: GepsioConsole [XBRL document]");
return;
}
ProcessXbrlWithGepsio(args[0]);
}

/// <summary>
/// Process a named XBRL document.
/// </summary>
/// <param name="xbrlFile">
/// The address of the XBRL document to process.
/// </param>
private static void ProcessXbrlWithGepsio(string xbrlFile)
{
try
{
var xbrlDoc = new XbrlDocument();
xbrlDoc.Load(xbrlFile);
foreach (var currentFragment in xbrlDoc.XbrlFragments)
{
DisplayFragmentStatistics(currentFragment);
WriteFactValue(currentFragment, "EntityRegistrantName");
WriteFactValue(currentFragment, "DocumentPeriodEndDate");
}
}
catch (XbrlException xbrle)
{
Console.WriteLine("ERROR: {0}", xbrle.Message);
}
}

/// <summary>
/// Display statistics relating to the loaded document fragment.
/// </summary>
/// <param name="currentFragment">
/// The XBRL fragment whose statistics should be published.
/// </param>
private static void DisplayFragmentStatistics(XbrlFragment currentFragment)
{
var factsCollection = currentFragment.Facts;
Console.WriteLine("Number of facts...: {0}", factsCollection.Count);
var unitsCollection = currentFragment.Units;
Console.WriteLine("Number of units...: {0}", unitsCollection.Count);
var contextsCollection = currentFragment.Contexts;
Console.WriteLine("Number of contexts: {0}", contextsCollection.Count);
}

/// <summary>
/// Display the value of a fact pulled from an XBRL fragment.
/// </summary>
/// <param name="currentFragment">
/// The fragment containing the fact to be found.
/// </param>
/// <param name="factName">
/// The name of the fact to be found.
/// </param>
private static void WriteFactValue(XbrlFragment currentFragment, string factName)
{
foreach (var currentFact in currentFragment.Facts)
{
if (currentFact.Name.Equals(factName) == true)
{
var currentFactAsItem = currentFact as Item;
Console.WriteLine("{0}: {1}", factName, currentFactAsItem.Value);
return;
}
}
}
}
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

I wanted to offer a few notes regarding this code:

  • This is a console application, and is intended to be run with a command line argument specifying the address of the XBRL document to be loaded, as in GepsioConsole http://www.sec.gov/Archives/edgar/data/21344/000104746911006790/ko-20110701.xml. Gepsio can work with documents stored on the Web, so HTTP-based document addresses are valid.
  • From Gepsio’s point of view, an XBRL document is a collection of fragments. The fragments idea was originally designed to support the notion of Inline XBRL, where a document may consist of multiple XBRL fragments. For standard XBRL documents, however, the entire document is an XBRL document, which makes up one “fragment”. This explains the “for each fragment in document” code in the ProcessCodeWithGepsio() method.
  • The WriteFactValue() method looks for a fact in the fragment’s collection of facts. Remember that, from an XBRL point of view, a fact is a type of item. In XBRL parlance, items can be facts, which have a single value, or tuples, which can have more than one value. Gepsio models this fact by defining a base class called Item and then deriving both Fact and Tuple from Item. This explains the cast back to Item in the code for WriteFactValue(). The cast may look a bit strange … perhaps I’ll revisit this in a later CTP.

As I developed this sample, I noticed that Gepsio performs as intended but, for larger documents, its performance can be improved. I’ll be addressing this shortly. Look for a future blog post where I discuss performance, where it could be improved, and how the improvements will be implemented. I will be using the code in this blog post as the showcase for the performance improvements, so you will be seeing this code again.