:pencil: Add more labels.

:pencil: Add more labels.

jsoniq version "1.0";
(:
 : Copyright 2015 28msec Inc.
 :)

(:~
 :
This module provides functionality for retrieving labels.

 :
 : @author Federico Cavalieri, Ghislain Fourny
 :)
module namespace labels = "http://28.io/modules/xbrl/labels";

import module namespace concepts = "http://28.io/modules/xbrl/concepts";
import module namespace entities = "http://28.io/modules/xbrl/entities";

declare namespace ver = "http://zorba.io/options/versioning";
declare option ver:module-version "1.0";

(:~
 : A helper variable holding the default language ("en-US")
 :)
declare variable $labels:AMERICAN_ENGLISH as xs:string := "en-US";

(:~
 : The standard label role.
 :)
declare variable $labels:STANDARD_LABEL_ROLE as xs:string :=
    "http://www.xbrl.org/2003/role/label";

(:~
 : The verbose label role.
 :)
declare variable $labels:VERBOSE_LABEL_ROLE as xs:string :=
    "http://www.xbrl.org/2003/role/verboseLabel";

declare variable $labels:standard-concepts as object* :=
(
    {
      Name: "xbrl:Concept",
      Label: "Concept [Axis]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Concept [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Konzept [Achse]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Concept [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "項目(Concept)"
        }
      ]
    },
    {
      Name: "xbrl:Period",
      Label: "Period [Axis]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Période [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Period [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Periode [Achse]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "期間(Period)"
        }
      ]
    },
    {
      Name: "xbrl:Entity",
      Label: "Entity [Axis]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Entität [Achse]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Entité [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Reporting Entity [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "企業名(Entity)"
        }
      ]
    },
    {
      Name: "xbrl28:Archive",
      Label: "Archiv [Achse]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Archive [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Archiv [Achse]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Archive [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "ファイル(Archive)"
        }
      ]
    },
    {
      Name: "fsa:FiscalYear",
      Label: "Fiscal Year [Axis]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Année fiscale [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Steuerjahr [Achse]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "会計年度(Fiscal Year)"
        }
      ]
    },
    {
      Name: "fsa:FiscalPeriod",
      Label: "Fiscal Period [Axis]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "会計期間(Fiscal Period)"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Fiscal Period [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Période fiscal [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Steuerperiode [Achse]"
        }
      ]
    },
    {
      Name: "fsa:FiscalPeriodType",
      Label: "Fiscal Period Type [Axis]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "会計期間タイプ(Fiscal Period Type)"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Fiscal Period Type [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Type de période fiscale [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Steuerperiodeart [Achse]"
        }
      ]
    }
);

(:~
 :
Retrieves all the labels with the given label role and language in the
 : matching concepts, one label for each concept.

 :
 :
Matching concepts are those which:
 :  - concept name and archive number match a given one;
 :  - component role matches a given one or is the default
 :    component role.
 :

 :
 :
The set of concepts to search in is specified as a parameter.

 :
 :
Language matching can either be exact, if no options are given,
 : or approximated, if at least one of the following options is given:

 :

 :
MatchDown: whether to match a more specific language, e.g.:
 :       "en" will match labels which language is "en" or "en-US".

 :
MatchUp: whether to match a less specific language, e.g.:
 :       "en-US" will match labels which language is "en-US" or "en".

 :
MatchAnyVariant: whether to match a different variant of the same
 :       language, e.g.: "en-US" will match labels which language is "en-US"
 :       or "en-UK".

 :

 :
 : @param $concept-names the concepts names.
 : @param $label-roles the label role.
 : @param $languages the label language.
 : @param $concepts the concepts in which the labels will be
 :                  searched.
 : @param $entities the entities in which entity labels will be searched.
 : @param $options Optional parameters to control language matching.
 :
 : @return the matching labels (one associated with each input concept, as
 : an object).
 :)
declare function labels:labels(
    $concept-names as string*,
    $label-roles as string*,
    $languages as string*,
    $concepts as object*,
    $entities as object*,
    $options as object?
  ) as object
{
  let $concepts := ($labels:standard-concepts, $concepts)
  return {|
    let $normalized-languages as string* := $languages ! labels:normalize-language($$)
    return (
      for $concept as object in $concepts
      where ($concepts:ALL_CONCEPT_NAMES, $concept.$concepts:NAME) = $concept-names
      group by $concept.$concepts:NAME
      let $concept-labels as object* :=
        $concept.$concepts:LABELS[][$$.Role = $label-roles]
      let $available-languages as string* :=
        distinct-values($concept-labels.Language)
      let $approximate-languages as string* := labels:approximate-languages(
        $available-languages,
        $normalized-languages,
        $options
      )
      let $english-languages as string* := labels:approximate-languages(
        $available-languages,
        "en",
        $options
      )
      let $perfect-matches as object* :=
        $concept-labels[$$.Language = $normalized-languages]
      let $approximate-matches as object* :=
        $concept-labels[$$.Language = $approximate-languages]
      let $english-matches as object* :=
        $concept-labels[$$.Language = $english-languages]
      let $label as string? :=
        ($perfect-matches.Value,
         $approximate-matches.Value,
         $english-matches.Value,
         $concept-labels.Value,
         $concept.Label)[not $$ instance of null][1]
      where exists($label)
      return {
        $concept.Name : $label
      },

      for $concept in $concept-names
      where not (($concepts:ALL_CONCEPT_NAMES, $concepts.$concepts:NAME) = $concept)
      let $label as string* :=
          switch(true)
            case starts-with($concept, "iso4217:")
            return substring($concept, 9)
            case $concept castable as date
            return format-date(date($concept), "[MNn] [D], [Y]", "en", (), ())
            case (every $date in tokenize($concept, "/") satisfies $date castable as date)
            return "From " || string-join(
                     for $date in tokenize($concept, "/")
                     return format-date(date($date), "[MNn] [D], [Y]", "en", (), ()),
                   " to ")
            case contains($concept, " ")
            return
              switch(true)
              case $concept = entities:eid($entities) and $normalized-languages = ("ja")
              return ($entities[entities:eid($$) = $concept].Profiles.SEC.EntityRegistrantName,
                       $entities[entities:eid($$) = $concept].Profiles.FSA.SubmitterName)[1]
              case $concept = entities:eid($entities)
              return ($entities[entities:eid($$) = $concept].Profiles.SEC.CompanyName,
                       $entities[entities:eid($$) = $concept].Profiles.FSA.SubmitterNameAlphabetic)[1]
              default return ()
           default return ()
      where exists($label)
      return {
        $concept : $label
      }
    )
  |}
};

(:~
 :
Retrieves all the labels with the given label role and language for
 : all concepts used in the fact and matching a concept in the list of
 : concepts. Concepts used in a fact include not only those from the
 : 'xbrl:Concept' aspect, but also Members of any custom axis.

 :
 :
Matching concepts are those which:
 :  - concept name matches a given one,
 :  - archive number matches that of a given component,
 :  - component role matches that of a given component or is the default
 :    component role.
 :

 :
 :
The set of concepts to search in is specified as a parameter.

 :
 :
Language matching can either be exact, if no options are given,
 : or approximated, if at least one of the following options is given:

 :

 :
MatchDown: whether to match a more specific language, e.g.:
 :       "en" will match labels which language is "en" or "en-US".

 :
MatchUp: whether to match a less specific language, e.g.:
 :       "en-US" will match labels which language is "en-US" or "en".

 :
MatchAnyVariant: whether to match a different variant of the same
 :       language, e.g.: "en-US" will match labels which language is "en-US"
 :       or "en-UK".

 :

 :
 : @param $facts a sequence of facts.
 : @param $label-role the label role.
 : @param $language the label language.
 : @param $concepts the concepts in which the labels will be
 :                  searched (in the version-7 format).
 : @param $options optional parameters to control language matching.
 :
 : @return an object with matching concepts as keys and labels as values.
 :)
declare function labels:labels-for-facts(
    $facts as object*,
    $label-role as string,
    $language as string,
    $concepts as object*,
    $entities as object*,
    $options as object?
  ) as object?
{
    for $facts in $facts
    group by $archive := $facts."Aspects"."xbrl28:Archive"
    let $concept-names as string* :=
        distinct-values((
          values($facts.Aspects),
          keys($facts.Aspects)
    )[$$ instance of string])
    return
        {|
            labels:labels(
                    $concept-names,
                    $label-role,
                    $language,
                    $concepts,
                    $entities,
                    $options)
        |}
};

(:~
 :
Normalizes the language code. This normalized language code can then
 : be used to find the right labels in the concepts collection.

 :
 : @param $language the language identifier.
 :
 : @return the normalized language.
 :)
declare function labels:normalize-language($language as string) as string
{
  replace(lower-case($language), "_", "-")
};

declare %private function labels:approximate-languages(
    $all-languages as string*,
    $normalized-language as string,
    $options as object?
  ) as string*
{
    if(not $options.MatchDown cast as boolean? eq false)
    then $all-languages[starts-with($$, $normalized-language)]
    else (),
    if(not $options.MatchUp cast as boolean? eq false)
    then $all-languages[starts-with($normalized-language, $$)]
    else (),
    if ((not $options.MatchAnyVariant cast as boolean? eq false) and contains($normalized-language, "-"))
    then
      $all-languages[starts-with($$, substring-before($normalized-language, "-") || "-")]
    else ()
};

jsoniq version "1.0";
(:
 : Copyright 2015 28msec Inc.
 :)

(:~
 :
This module provides functionality for retrieving labels.

 :
 : @author Federico Cavalieri, Ghislain Fourny
 :)
module namespace labels = "http://28.io/modules/xbrl/labels";

import module namespace concepts = "http://28.io/modules/xbrl/concepts";
import module namespace entities = "http://28.io/modules/xbrl/entities";

declare namespace ver = "http://zorba.io/options/versioning";
declare option ver:module-version "1.0";

(:~
 : A helper variable holding the default language ("en-US")
 :)
declare variable $labels:AMERICAN_ENGLISH as xs:string := "en-US";

(:~
 : The standard label role.
 :)
declare variable $labels:STANDARD_LABEL_ROLE as xs:string :=
    "http://www.xbrl.org/2003/role/label";

(:~
 : The verbose label role.
 :)
declare variable $labels:VERBOSE_LABEL_ROLE as xs:string :=
    "http://www.xbrl.org/2003/role/verboseLabel";

declare variable $labels:standard-concepts as object* :=
(
    {
      Name: "xbrl:Concept",
      Label: "Concept [Axis]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Concept [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Konzept [Achse]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Concept [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "項目(Concept)"
        }
      ]
    },
    {
      Name: "xbrl:Period",
      Label: "Period [Axis]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Période [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Period [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Periode [Achse]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "期間(Period)"
        }
      ]
    },
    {
      Name: "xbrl:Entity",
      Label: "Entity [Axis]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Entität [Achse]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Entité [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Reporting Entity [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "企業名(Entity)"
        }
      ]
    },
    {
      Name: "xbrl28:Archive",
      Label: "Archiv [Achse]",
      Kind: "Dimension",
      IsNillable: true,
      IsAbstract: true,
      PeriodType: "duration",
      SubstitutionGroup: "xbrldt:dimensionItem",
      DataType: "nonnum:domainItemType",
      BaseType: "string",
      ClosestSchemaBuiltinType: "xs:string",
      IsTextBlock: false,
      Labels: [
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "fr",
          Value: "Archive [Axe]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "de",
          Value: "Archiv [Achse]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "en-us",
          Value: "Archive [Axis]"
        },
        {
          Role: "http://www.xbrl.org/2003/role/label",
          Language: "ja",
          Value: "ファイル(Archive)"
        }
      ]
    }
);

(:~
 :
Retrieves all the labels with the given label role and language in the
 : matching concepts, one label for each concept.

 :
 :
Matching concepts are those which:
 :  - concept name and archive number match a given one;
 :  - component role matches a given one or is the default
 :    component role.
 :

 :
 :
The set of concepts to search in is specified as a parameter.

 :
 :
Language matching can either be exact, if no options are given,
 : or approximated, if at least one of the following options is given:

 :

 :
MatchDown: whether to match a more specific language, e.g.:
 :       "en" will match labels which language is "en" or "en-US".

 :
MatchUp: whether to match a less specific language, e.g.:
 :       "en-US" will match labels which language is "en-US" or "en".

 :
MatchAnyVariant: whether to match a different variant of the same
 :       language, e.g.: "en-US" will match labels which language is "en-US"
 :       or "en-UK".

 :

 :
 : @param $concept-names the concepts names.
 : @param $label-roles the label role.
 : @param $languages the label language.
 : @param $concepts the concepts in which the labels will be
 :                  searched.
 : @param $entities the entities in which entity labels will be searched.
 : @param $options Optional parameters to control language matching.
 :
 : @return the matching labels (one associated with each input concept, as
 : an object).
 :)
declare function labels:labels(
    $concept-names as string*,
    $label-roles as string*,
    $languages as string*,
    $concepts as object*,
    $entities as object*,
    $options as object?
  ) as object
{
  let $concepts := ($concepts, $labels:standard-concepts)
  return {|
    let $normalized-languages as string* := $languages ! labels:normalize-language($$)
    return (
      for $concept as object in $concepts
      where ($concepts:ALL_CONCEPT_NAMES, $concept.$concepts:NAME) = $concept-names
      group by $concept.$concepts:NAME
      let $concept-labels as object* :=
        $concept.$concepts:LABELS[][$$.Role = $label-roles]
      let $available-languages as string* :=
        distinct-values($concept-labels.Language)
      let $approximate-languages as string* := labels:approximate-languages(
        $available-languages,
        $normalized-languages,
        $options
      )
      let $english-languages as string* := labels:approximate-languages(
        $available-languages,
        "en",
        $options
      )
      let $perfect-matches as object* :=
        $concept-labels[$$.Language = $normalized-languages]
      let $approximate-matches as object* :=
        $concept-labels[$$.Language = $approximate-languages]
      let $english-matches as object* :=
        $concept-labels[$$.Language = $english-languages]
      let $label as string? :=
        ($perfect-matches.Value,
         $approximate-matches.Value,
         $english-matches.Value,
         $concept-labels.Value,
         $concept.Label)[not $$ instance of null][1]
      where exists($label)
      return {
        $concept.Name : $label
      },

      for $concept in $concept-names
      where not (($concepts:ALL_CONCEPT_NAMES, $concepts.$concepts:NAME) = $concept)
      let $label as string* :=
          switch(true)
            case starts-with($concept, "iso4217:")
            return substring($concept, 9)
            case $concept castable as date
            return format-date(date($concept), "[MNn] [D], [Y]", "en", (), ())
            case (every $date in tokenize($concept, "/") satisfies $date castable as date)
            return "From " || string-join(
                     for $date in tokenize($concept, "/")
                     return format-date(date($date), "[MNn] [D], [Y]", "en", (), ()),
                   " to ")
            case contains($concept, " ")
            return
              switch(true)
              case $concept = entities:eid($entities) and $normalized-languages = ("ja")
              return ($entities[entities:eid($$) = $concept].Profiles.SEC.EntityRegistrantName,
                       $entities[entities:eid($$) = $concept].Profiles.FSA.SubmitterName)[1]
              case $concept = entities:eid($entities)
              return ($entities[entities:eid($$) = $concept].Profiles.SEC.CompanyName,
                       $entities[entities:eid($$) = $concept].Profiles.FSA.SubmitterNameAlphabetic)[1]
              default return ()
           default return ()
      where exists($label)
      return {
        $concept : $label
      }
    )
  |}
};

(:~
 :
Retrieves all the labels with the given label role and language for
 : all concepts used in the fact and matching a concept in the list of
 : concepts. Concepts used in a fact include not only those from the
 : 'xbrl:Concept' aspect, but also Members of any custom axis.

 :
 :
Matching concepts are those which:
 :  - concept name matches a given one,
 :  - archive number matches that of a given component,
 :  - component role matches that of a given component or is the default
 :    component role.
 :

 :
 :
The set of concepts to search in is specified as a parameter.

 :
 :
Language matching can either be exact, if no options are given,
 : or approximated, if at least one of the following options is given:

 :

 :
MatchDown: whether to match a more specific language, e.g.:
 :       "en" will match labels which language is "en" or "en-US".

 :
MatchUp: whether to match a less specific language, e.g.:
 :       "en-US" will match labels which language is "en-US" or "en".

 :
MatchAnyVariant: whether to match a different variant of the same
 :       language, e.g.: "en-US" will match labels which language is "en-US"
 :       or "en-UK".

 :

 :
 : @param $facts a sequence of facts.
 : @param $label-role the label role.
 : @param $language the label language.
 : @param $concepts the concepts in which the labels will be
 :                  searched (in the version-7 format).
 : @param $options optional parameters to control language matching.
 :
 : @return an object with matching concepts as keys and labels as values.
 :)
declare function labels:labels-for-facts(
    $facts as object*,
    $label-role as string,
    $language as string,
    $concepts as object*,
    $entities as object*,
    $options as object?
  ) as object?
{
    for $facts in $facts
    group by $archive := $facts."Aspects"."xbrl28:Archive"
    let $concept-names as string* :=
        distinct-values((
          values($facts.Aspects),
          keys($facts.Aspects)
    )[$$ instance of string])
    return
        {|
            labels:labels(
                    $concept-names,
                    $label-role,
                    $language,
                    $concepts,
                    $entities,
                    $options)
        |}
};

(:~
 :
Normalizes the language code. This normalized language code can then
 : be used to find the right labels in the concepts collection.

 :
 : @param $language the language identifier.
 :
 : @return the normalized language.
 :)
declare function labels:normalize-language($language as string) as string
{
  replace(lower-case($language), "_", "-")
};

declare %private function labels:approximate-languages(
    $all-languages as string*,
    $normalized-language as string,
    $options as object?
  ) as string*
{
    if(not $options.MatchDown cast as boolean? eq false)
    then $all-languages[starts-with($$, $normalized-language)]
    else (),
    if(not $options.MatchUp cast as boolean? eq false)
    then $all-languages[starts-with($normalized-language, $$)]
    else (),
    if ((not $options.MatchAnyVariant cast as boolean? eq false) and contains($normalized-language, "-"))
    then
      $all-languages[starts-with($$, substring-before($normalized-language, "-") || "-")]
    else ()
};


:green_heart: Fix warning.

:green_heart: Fix warning.

jsoniq version "1.0";

import module namespace components = "http://28.io/modules/xbrl/components";
import module namespace filings = "http://28.io/modules/xbrl/profiles/sec/filings";
import module namespace sec = "http://28.io/modules/xbrl/profiles/sec/core";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace conversion = "http://28.io/modules/xbrl/conversion";
import module namespace networks = "http://28.io/modules/xbrl/networks";
import module namespace concept-maps = "http://28.io/modules/xbrl/concept-maps";
import module namespace rules = "http://28.io/modules/xbrl/rules";
import module namespace labels = "http://28.io/modules/xbrl/labels";

import module namespace sec-networks = "http://28.io/modules/xbrl/profiles/sec/networks";
import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

import module namespace config = "http://apps.28.io/config";
import module namespace session = "http://apps.28.io/session";
import module namespace api = "http://apps.28.io/api";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token              as string? external;
declare  %rest:env                              variable $request-uri        as string  external;
declare  %rest:case-insensitive                 variable $format             as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode         as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker             as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear         as string* external := "LATEST";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod       as string* external := "FY";
declare  %rest:case-insensitive %rest:distinct  variable $eid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $networkIdentifier  as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $cid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $concept            as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $reportElement      as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $disclosure         as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $label              as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $rollup             as string* external;
declare  %rest:case-insensitive                 variable $map                as string? external;
declare  %rest:case-insensitive                 variable $validate           as boolean external := false;
declare  %rest:case-insensitive                 variable $labels             as boolean external := false;
declare  %rest:case-insensitive                 variable $merge              as boolean external := false;
declare  %rest:case-insensitive                 variable $additional-rules   as string? external;
declare  %rest:case-insensitive                 variable $profile-name       as string  external := $config:profile-name;
declare  %rest:case-insensitive %rest:distinct  variable $role               as string* external;
declare  %rest:case-insensitive                 variable $language           as string  external := "en-US";

session:audit-call($token);

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $fiscalYear as integer* := api:preprocess-fiscal-years($fiscalYear)
let $fiscalPeriod as string* := api:preprocess-fiscal-periods($fiscalPeriod)
let $tag as string* := api:preprocess-tags($tag)
let $reportElement := ($reportElement, $concept)
let $networkIdentifier := distinct-values(($networkIdentifier, $role))

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities as object* := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic, ())

let $archives as object* := multiplexer:filings(
  $profile-name,
  $entities,
  $fiscalPeriod,
  $fiscalYear,
  $aid)

let $entities as object* :=
    ($entities[entities:eid($$) = $archives.Entity],
    let $not-found := $archives.Entity[not entities:eid($entities) = $$]
    where exists($not-found)
    return entities:entities($not-found))

let $components as object* :=
    multiplexer:components(
      $profile-name,
      $archives,
      $cid,
      $reportElement,
      $disclosure,
      $networkIdentifier,
    $label)

let $component as object? := if($merge) then components:merge($components) else $components[1]
let $cid as string? := string-join($components ! components:cid($$), "--")
let $rules as object* := if(exists($additional-rules)) then rules:rules($additional-rules) else ()

(: Fact resolution :)
let $facts :=
    if (exists($rollup) and $profile-name eq "sec")
         then
             let $calc-network := networks:networks-for-components-and-short-names($component, $networks:CALCULATION_NETWORK)
             let $hc := hypercubes:hypercubes-for-components($component)[]
             let $hc := hypercubes:modify-hypercube($hc, {
                 "sec:FiscalYear" : { Type: "integer", Default: null },
                 "sec:FiscalPeriod" : { Type: "string", Default: null },
                 "sec:FiscalPeriodType" : { Type: "string", Default: null }
             })
             let $options as object? := if(exists($rules)) then { Rules: [ $rules ] } else ()
             let $p := hypercubes:populate-networks-with-facts($calc-network, $hc, $archives, $options)
             let $map := concept-maps:concept-maps($map)
             let $concepts :=
                if (not $map instance of null)
                then
                    for $d in $rollup[]
                    return
                        descendant-objects($p)[$$.Name = ($map.Trees[])[$$.Name eq $d].To[].Name][1].To[].Name
                else
                    for $d in $rollup[]
                    return ($d, keys(descendant-objects($p)[$$.Name eq $d].To))
             return sec:facts-for-archives-and-concepts($archives, $concepts, { Hypercube: $hc })
         else components:facts(
            $component,
            {|
                {
                    FilterOverride : {
                        "sec:FiscalPeriod" : { Type: "string", Default: null },
                        "sec:FiscalYear" : { Type: "string", Default: null },
                        "sec:FiscalPeriodType" : { Type: "string", Default: null }
                    }
                }[$profile-name eq "sec"],
                { Validate: $validate },
                { Rules : [ $rules ] }[exists($rules)]
            |}
        )

let $facts :=
    if(not $labels)
    then $facts
    else
        (: if labels are requested by the labels=true parameter then also add labels for concepts :)
        let $concepts as object* := $component.Concepts[]
        for $fact in $facts
        let $labels := labels:labels-for-facts(
          $fact,
          $labels:STANDARD_LABEL_ROLE,
          $language,
          $concepts,
          $entities,
          ()
        )
        return
            {|
                trim($fact, "Labels"),
                { Labels : $labels }
            |}
let $facts := api:normalize-facts($facts)

let $result :=
        {|
            {
                CIK : entities:eid($entities),
                EntityRegistrantName : $entities.Profiles.SEC.CompanyName,
                TableName : sec-networks:tables($component, {IncludeImpliedTable: true}).Name,
                Label : $component.Label,
                AccessionNumber : $component.Archive,
                FormType : $archives.Profiles.SEC.FormType,
                FiscalPeriod : $archives.Profiles.SEC.Fiscal.DocumentFiscalPeriodFocus,
                FiscalYear : $archives.Profiles.SEC.Fiscal.DocumentFiscalYearFocus,
                AcceptanceDatetime : ($archives ! filings:acceptance-dateTimes($$)),
                NetworkIdentifier: $component.Role,
                Disclosure : $component.Profiles.SEC.Disclosure,
                FactTable : [ $facts ]
            }[$profile-name eq "sec"],
            {
                Archive : $component.Archive,
                Role: $component.Role,
                TableName : keys($component.Hypercubes),
                Label : $component.Label,
                FactTable : [ $facts ]
            }[$profile-name ne "sec"]
        |}

let $comment := {
    NumFacts : count($facts),
    TotalNumFacts: session:num-facts(),
    TotalNumArchives: session:num-archives(),
    TotalNumEntities: session:num-entities()
}

let $serializers := {
    to-xml : function($res as object) as node() {
        switch($profile-name)
        case "sec" return <FactTable entityRegistrantName="{$res.EntityRegistrantName}"
            cik="{$res.CIK}"
            tableName="{$res.TableName}"
            label="{$res.Label}"
            accessionNumber="{$res.AccessionNumber}"
            networkIdentifier="{$res.NetworkIdentifier}"
            formType="{$res.FormType}"
            fiscalPeriod="{$res.FiscalPeriod}"
            fiscalYear="{$res.FiscalYear}"
            acceptanceDatetime="{$res.AcceptanceDatetime}"
            disclosure="{$res.Disclosure}"
            >{
            conversion:facts-to-xml($res.FactTable[], { Caller: "Component" })
        }</FactTable>
        default return <FactTable
            tableName="{$res.TableName}"
            label="{$res.Label}"
            archive="{$res.Archive}"
            role="{$res.Role}"
            >{
            conversion:facts-to-xml($res.FactTable[], { Caller: "Component" })
        }</FactTable>
    },
    to-csv : function($res as object) as string {
        string-join(conversion:facts-to-csv($res.FactTable[], { Caller: "Component"}))
    }
}
return api:serialize($result, $comment, $serializers, $format, "facttable-" || $cid)

jsoniq version "1.0";

import module namespace components = "http://28.io/modules/xbrl/components";
import module namespace filings = "http://28.io/modules/xbrl/profiles/sec/filings";
import module namespace sec = "http://28.io/modules/xbrl/profiles/sec/core";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace conversion = "http://28.io/modules/xbrl/conversion";
import module namespace networks = "http://28.io/modules/xbrl/networks";
import module namespace concept-maps = "http://28.io/modules/xbrl/concept-maps";
import module namespace concepts = "http://28.io/modules/xbrl/concepts";
import module namespace facts = "http://28.io/modules/xbrl/facts";
import module namespace rules = "http://28.io/modules/xbrl/rules";
import module namespace labels = "http://28.io/modules/xbrl/labels";

import module namespace sec-networks = "http://28.io/modules/xbrl/profiles/sec/networks";
import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

import module namespace config = "http://apps.28.io/config";
import module namespace session = "http://apps.28.io/session";
import module namespace api = "http://apps.28.io/api";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token              as string? external;
declare  %rest:env                              variable $request-uri        as string  external;
declare  %rest:case-insensitive                 variable $format             as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode         as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker             as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear         as string* external := "LATEST";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod       as string* external := "FY";
declare  %rest:case-insensitive %rest:distinct  variable $eid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $networkIdentifier  as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $cid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $concept            as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $reportElement      as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $disclosure         as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $label              as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $rollup             as string* external;
declare  %rest:case-insensitive                 variable $map                as string? external;
declare  %rest:case-insensitive                 variable $validate           as boolean external := false;
declare  %rest:case-insensitive                 variable $labels             as boolean external := false;
declare  %rest:case-insensitive                 variable $merge              as boolean external := false;
declare  %rest:case-insensitive                 variable $additional-rules   as string? external;
declare  %rest:case-insensitive                 variable $profile-name       as string  external := $config:profile-name;
declare  %rest:case-insensitive %rest:distinct  variable $role               as string* external;
declare  %rest:case-insensitive                 variable $language           as string  external := "en-US";

session:audit-call($token);

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $fiscalYear as integer* := api:preprocess-fiscal-years($fiscalYear)
let $fiscalPeriod as string* := api:preprocess-fiscal-periods($fiscalPeriod)
let $tag as string* := api:preprocess-tags($tag)
let $reportElement := ($reportElement, $concept)
let $networkIdentifier := distinct-values(($networkIdentifier, $role))

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities as object* := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic, ())

let $archives as object* := multiplexer:filings(
  $profile-name,
  $entities,
  $fiscalPeriod,
  $fiscalYear,
  $aid)

let $entities as object* :=
    ($entities[entities:eid($$) = $archives.Entity],
    let $not-found := $archives.Entity[not entities:eid($entities) = $$]
    where exists($not-found)
    return entities:entities($not-found))

let $components as object* :=
    multiplexer:components(
      $profile-name,
      $archives,
      $cid,
      $reportElement,
      $disclosure,
      $networkIdentifier,
    $label)

let $component as object? := if($merge) then components:merge($components) else $components[1]
let $cid as string? := string-join($components ! components:cid($$), "--")
let $rules as object* := if(exists($additional-rules)) then rules:rules($additional-rules) else ()

(: Fact resolution :)
let $facts :=
    if (exists($rollup) and $profile-name eq "sec")
         then
             let $calc-network := networks:networks-for-components-and-short-names($component, $networks:CALCULATION_NETWORK)
             let $hc := hypercubes:hypercubes-for-components($component)[]
             let $hc := hypercubes:modify-hypercube($hc, {
                 "sec:FiscalYear" : { Type: "integer", Default: null },
                 "sec:FiscalPeriod" : { Type: "string", Default: null },
                 "sec:FiscalPeriodType" : { Type: "string", Default: null }
             })
             let $options as object? := if(exists($rules)) then { Rules: [ $rules ] } else ()
             let $p := hypercubes:populate-networks-with-facts($calc-network, $hc, $archives, $options)
             let $map := concept-maps:concept-maps($map)
             let $concepts :=
                if (not $map instance of null)
                then
                    for $d in $rollup[]
                    return
                        descendant-objects($p)[$$.Name = ($map.Trees[])[$$.Name eq $d].To[].Name][1].To[].Name
                else
                    for $d in $rollup[]
                    return ($d, keys(descendant-objects($p)[$$.Name eq $d].To))
             return sec:facts-for-archives-and-concepts($archives, $concepts, { Hypercube: $hc })
         else components:facts(
            $component,
            {|
                {
                    FilterOverride : {
                        "sec:FiscalPeriod" : { Type: "string", Default: null },
                        "sec:FiscalYear" : { Type: "string", Default: null },
                        "sec:FiscalPeriodType" : { Type: "string", Default: null }
                    }
                }[$profile-name eq "sec"],
                { Validate: $validate },
                { Rules : [ $rules ] }[exists($rules)]
            |}
        )

let $facts :=
    if(not $labels)
    then $facts
    else
        (: if labels are requested by the labels=true parameter then also add labels for concepts :)
        let $concepts as object* := $component.Concepts[]
        for $fact in $facts
        let $labels := labels:labels-for-facts(
          $fact,
          $labels:STANDARD_LABEL_ROLE,
          $language,
          $concepts,
          $entities,
          ()
        )
        return
            {|
                trim($fact, "Labels"),
                { Labels : $labels }
            |}
let $facts := api:normalize-facts($facts)

let $result :=
        {|
            {
                CIK : entities:eid($entities),
                EntityRegistrantName : $entities.Profiles.SEC.CompanyName,
                TableName : sec-networks:tables($component, {IncludeImpliedTable: true}).Name,
                Label : $component.Label,
                AccessionNumber : $component.Archive,
                FormType : $archives.Profiles.SEC.FormType,
                FiscalPeriod : $archives.Profiles.SEC.Fiscal.DocumentFiscalPeriodFocus,
                FiscalYear : $archives.Profiles.SEC.Fiscal.DocumentFiscalYearFocus,
                AcceptanceDatetime : ($archives ! filings:acceptance-dateTimes($$)),
                NetworkIdentifier: $component.Role,
                Disclosure : $component.Profiles.SEC.Disclosure,
                FactTable : [ $facts ]
            }[$profile-name eq "sec"],
            {
                Archive : $component.Archive,
                Role: $component.Role,
                TableName : keys($component.Hypercubes),
                Label : $component.Label,
                FactTable : [ $facts ]
            }[$profile-name ne "sec"]
        |}

let $comment := {
    NumFacts : count($facts),
    TotalNumFacts: session:num-facts(),
    TotalNumArchives: session:num-archives(),
    TotalNumEntities: session:num-entities()
}

let $serializers := {
    to-xml : function($res as object) as node() {
        switch($profile-name)
        case "sec" return <FactTable entityRegistrantName="{$res.EntityRegistrantName}"
            cik="{$res.CIK}"
            tableName="{$res.TableName}"
            label="{$res.Label}"
            accessionNumber="{$res.AccessionNumber}"
            networkIdentifier="{$res.NetworkIdentifier}"
            formType="{$res.FormType}"
            fiscalPeriod="{$res.FiscalPeriod}"
            fiscalYear="{$res.FiscalYear}"
            acceptanceDatetime="{$res.AcceptanceDatetime}"
            disclosure="{$res.Disclosure}"
            >{
            conversion:facts-to-xml($res.FactTable[], { Caller: "Component" })
        }</FactTable>
        default return <FactTable
            tableName="{$res.TableName}"
            label="{$res.Label}"
            archive="{$res.Archive}"
            role="{$res.Role}"
            >{
            conversion:facts-to-xml($res.FactTable[], { Caller: "Component" })
        }</FactTable>
    },
    to-csv : function($res as object) as string {
        string-join(conversion:facts-to-csv($res.FactTable[], { Caller: "Component"}))
    }
}
return api:serialize($result, $comment, $serializers, $format, "facttable-" || $cid)


Fixed merge conflict.

Fixed merge conflict.

import module namespace util = "http://secxbrl.info/modules/util";

import module namespace request = "http://www.28msec.com/modules/http-request";
import module namespace session = "http://apps.28.io/session";
import module namespace entities = "http://xbrl.io/modules/bizql/entities";
import module namespace hypercubes = "http://xbrl.io/modules/bizql/hypercubes";
declare namespace components = "http://xbrl.io/modules/bizql/components";
import module namespace components2 = "http://xbrl.io/modules/bizql/components2";
import module namespace reports = "http://xbrl.io/modules/bizql/reports";
import module namespace concept-maps = "http://xbrl.io/modules/bizql/concept-maps";

import module namespace companies2 = "http://xbrl.io/modules/bizql/profiles/sec/companies2";
import module namespace fiscal-core = "http://xbrl.io/modules/bizql/profiles/sec/fiscal/core";
import module namespace fiscal-core2 = "http://xbrl.io/modules/bizql/profiles/sec/fiscal/core2";

import module namespace mongo = "http://www.28msec.com/modules/mongodb";
import module namespace credentials = "http://www.28msec.com/modules/credentials";

import module namespace csv = "http://zorba.io/modules/json-csv";
import module namespace seq = "http://zorba.io/modules/sequence";

declare namespace concepts = "http://www.28msec.com/modules/bizql/concepts";

declare function local:to-csv($concepts as item*, $onlyNames as boolean) as string
{
    if ($onlyNames)
    then
        string-join(("Name", $concepts), "
")
    else
        string-join(csv:serialize($concepts, { serialize-null-as : "" }))
};

declare function local:to-xml($concepts as item*, $onlyNames as boolean) as element()*
{
    for $c in $concepts
    return
        <ReportElement>{
             if ($onlyNames)
             then
                <Name>{$c}</Name>
             else (
                <Name>{$c.Name}</Name>,
                <Label>{$c.Label}</Label>,
                <IsNillable>{$c.IsNillable}</IsNillable>,
                <IsAbstract>{$c.IsAbstract}</IsAbstract>,
                <PeriodType>{$c.PeriodType}</PeriodType>,
                <Balance>{$c.Balance}</Balance>,
                <SubstitutionGroup>{$c.SubstitutionGroup}</SubstitutionGroup>,
                <DataType>{$c.DataType}</DataType>,
                <BaseType>{$c.BaseType}</BaseType>,
                <ClosestSchemaBuiltinType>{$c.ClosestSchemaBuiltinType}</ClosestSchemaBuiltinType>,
                <IsTextBlock>{$c.IsTextBlock}</IsTextBlock>,
                <ComponentRole>{$c.ComponentRole}</ComponentRole>,
                <ComponentLabel>{$c.ComponentLabel}</ComponentLabel>,
                <AccessionNumber>{$c.AccessionNumber}</AccessionNumber>,
                <CIK>{$c.CIK}</CIK>,
                <EntityRegistrantName>{$c.EntityRegistrantName}</EntityRegistrantName>,
                <FiscalYear>{$c.FiscalYear}</FiscalYear>,
                <FiscalPeriod>{$c.FiscalPeriod}</FiscalPeriod>
             )
        }</ReportElement>
};

declare function local:concepts-for-archives($aids as string*) as object*
{
    let $conn :=
      let $credentials := credentials:credentials("MongoDB", "xbrl")
      return
        try {
            mongo:connect($credentials)
        } catch mongo:* {
            error(QName("concepts:CONNECTION-FAILED"), $err:description)
        }
    return
        mongo:find($conn, "concepts",
        {
            "Archive": { "$in" : [ $aids ] }
        })
};

declare function local:concepts-for-archives($aids as string*, $names as string*, $map as string?) as object*
{
    let $conn :=
      let $credentials := credentials:credentials("MongoDB", "xbrl")
      return
        try {
            mongo:connect($credentials)
        } catch mongo:* {
            error(QName("concepts:CONNECTION-FAILED"), $err:description)
        }
    let $map := if (exists($map)) then concept-maps:concept-maps($map) else ()

    let $all-concepts-computable-by-maps as string* := keys($map.Trees)

    let $concepts-computable-by-maps as object* :=
        for $c in $names[$$ = $all-concepts-computable-by-maps]
        return $map.Trees.$c

    let $mapped-names := (keys($concepts-computable-by-maps.To ), $concepts-computable-by-maps.To [].Name)

    let $concepts-not-computable-by-maps as string* := seq:value-except($names, $mapped-names)
    let $results-not-computed-by-maps := mongo:find($conn, "concepts",
        {
            "Name" : { "$in" : [ $concepts-not-computable-by-maps ] },
            "Archive": { "$in" : [ $aids ] }
        })
    let $results-computed-by-maps :=
        for $c in mongo:find($conn, "concepts",
            {
                "Name" : { "$in" : [ $mapped-names ] },
                "Archive": { "$in" : [ $aids ] }
            })
        group by $c.Component
        let $c := $c[1]
        let $map-concept := (for $candidate in $concepts-computable-by-maps
                            where $c.Name = (keys($candidate.To), $candidate.To[].Name)
                            return $candidate)[1]
        return
            copy $n := $c
            modify (
                replace value of json $n.Name with $map-concept.Name,
                insert json  { Origin : $c.Name } into $n)
            return $n
    return ($results-not-computed-by-maps, $results-computed-by-maps)
};

declare function local:concepts-for-archives-and-labels($aids as string*, $labels as string) as object*
{
    let $conn :=
        let $credentials := credentials:credentials("MongoDB", "xbrl")
        return
            try {
                mongo:connect($credentials)
            } catch mongo:* {
                error(QName("components:CONNECTION-FAILED"), $err:description)
            }
    return mongo:run-cmd-deterministic(
        $conn,
        {
            "text" : "concepts",
            "filter" : { "Archive" : { "$in" : [ $aids ] } },
            "search" : $labels,
            "limit" : 100,
            "score" : { "$meta" : "textScore" },
            "sort" : { score: { "$meta" : "textScore" } }
        }).results[].obj
};

session:audit-call();

(: Query parameters :)
let $format as string?         := request:param-values("format")
let $ciks as string*           := distinct-values(request:param-values("cik"))
let $tags as string*           := distinct-values(request:param-values("tag"))
let $tickers as string*        := distinct-values(request:param-values("ticker"))
let $sics as string*           := distinct-values(request:param-values("sic"))
let $fiscalYears as string*    := distinct-values(request:param-values("fiscalYear", "LATEST"))
let $fiscalPeriods as string*  := distinct-values(request:param-values("fiscalPeriod", "FY"))
let $aids as string*           := distinct-values(request:param-values("aid"))
let $labels as string*         := request:param-values("label")
let $map as string?            := request:param-values("map")
let $report as string?         := request:param-values("report")
let $names as string*          := request:param-values("name")

(: Post-processing :)
let $format as string? := (: backwards compatibility, to be deprecated  :)
    lower-case(($format, substring-after(request:path(), ".jq."))[1])
let $tags as string* := (: backwards compatibility, to be deprecated :)
    distinct-values($tags ! upper-case($$))
let $fiscalYears as integer* :=
    for $fy in $fiscalYears ! upper-case($$)
    return switch($fy)
           case "LATEST" return $fiscal-core2:LATEST_FISCAL_YEAR
           case "ALL" return $fiscal-core:ALL_FISCAL_YEARS
           default return if($fy castable as integer) then integer($fy) else ()
let $fiscalPeriods as string* :=
    for $fp in $fiscalPeriods ! upper-case($$)
    return switch($fp)
           case "ALL" return $fiscal-core:ALL_FISCAL_PERIODS
           default return $fp

(: Object resolution :)
let $entities :=
    companies2:companies(
        $ciks,
        $tags,
        $tickers,
        $sics)
let $archives as object* := fiscal-core2:filings(
    $entities,
    $fiscalPeriods,
    $fiscalYears,
    $aids)
let $entities    := entities:entities($archives.Entity)
let $onlyNames   := let $o := request:param-values("onlyNames")[1] return if (exists($o)) then ($o cast as boolean) else false
let $map as item* :=
    if(exists($report))
    then reports:concept-map($report)
    else $map

let $concepts := if (exists($names))
                  then local:concepts-for-archives($archives._id, $names, $map)
                  else if (exists($labels))
                  then local:concepts-for-archives-and-labels($archives._id, $labels[1])
                  else local:concepts-for-archives($archives._id)
let $result := {
    ReportElements : [
        if ($onlyNames)
        then distinct-values($concepts.Name)
        else
            let $all-aids := $concepts.Archive
            let $roles := $concepts.Role
            let $components := components2:components-for-archives-and-roles($all-aids, $roles)
            return
            for $concept in $concepts
            group by $archive := $concept.Archive,  $role := $concept.Role
            let $component := $components[$$.Archive eq $archive and $$.Role eq $role]
            let $default-hc := hypercubes:hypercubes-for-components($component, "xbrl:DefaultHypercube")
            let $members := $default-hc.Aspects."xbrl:Concept".Domains."xbrl:ConceptDomain".Members
            let $archive := $archives[$$._id eq $archive]
            let $entity := $entities[$$._id eq $archive.Entity]
            let $metadata := {|
                { Origin : $concept.Origin }[exists($concept.Origin)],
                {
                    Name: $concept.Name,
                    ComponentRole : $component.Role,
                    ComponentLabel : $component.Label,
                    AccessionNumber : $archive._id,
                    CIK : $entity._id,
                    EntityRegistrantName : $entity.Profiles.SEC.CompanyName,
                    FiscalYear : $archive.Profiles.SEC.Fiscal.DocumentFiscalYearFocus,
                    FiscalPeriod : $archive.Profiles.SEC.Fiscal.DocumentFiscalPeriodFocus
                }
            |}
            for $concept in $concept
            let $original-name := ($concept.Origin, $concept.Name)[1]
            return {|
                trim($members.$original-name, "Name"),
                $metadata
            |}
    ]
}
let $comment := {
    NumConcepts: count($concepts),
    TotalNumConcepts: session:num-concepts()
}
let $serializers := {
    to-xml : function($res as object) as node() {
        <ReportElements>{
                    local:to-xml($res.ReportElements[], $onlyNames)
                }</ReportElements>
    },
    to-csv : function($res as object) as string {
        local:to-csv($res.ReportElements[], $onlyNames)
    }
}
let $results := util:serialize($result, $comment, $serializers, $format, "report-elements")
return
    util:check-and-return-results($entities, $results, $format)

import module namespace util = "http://secxbrl.info/modules/util";

import module namespace request = "http://www.28msec.com/modules/http-request";
import module namespace session = "http://apps.28.io/session";
import module namespace entities = "http://xbrl.io/modules/bizql/entities";
import module namespace hypercubes = "http://xbrl.io/modules/bizql/hypercubes";
declare namespace components = "http://xbrl.io/modules/bizql/components";
import module namespace components2 = "http://xbrl.io/modules/bizql/components2";
import module namespace reports = "http://xbrl.io/modules/bizql/reports";
import module namespace concept-maps = "http://xbrl.io/modules/bizql/concept-maps";

import module namespace companies2 = "http://xbrl.io/modules/bizql/profiles/sec/companies2";
import module namespace fiscal-core = "http://xbrl.io/modules/bizql/profiles/sec/fiscal/core";
import module namespace fiscal-core2 = "http://xbrl.io/modules/bizql/profiles/sec/fiscal/core2";

import module namespace mongo = "http://www.28msec.com/modules/mongodb";
import module namespace credentials = "http://www.28msec.com/modules/credentials";

import module namespace csv = "http://zorba.io/modules/json-csv";
import module namespace seq = "http://zorba.io/modules/sequence";

declare namespace concepts = "http://www.28msec.com/modules/bizql/concepts";

declare function local:to-csv($concepts as item*, $onlyNames as boolean) as string
{
    if ($onlyNames)
    then
        string-join(("Name", $concepts), "
")
    else
        string-join(csv:serialize($concepts, { serialize-null-as : "" }))
};

declare function local:to-xml($concepts as item*, $onlyNames as boolean) as element()*
{
    for $c in $concepts
    return
        <ReportElement>{
             if ($onlyNames)
             then
                <Name>{$c}</Name>
             else (
                <Name>{$c.Name}</Name>,
                <Label>{$c.Label}</Label>,
                <IsNillable>{$c.IsNillable}</IsNillable>,
                <IsAbstract>{$c.IsAbstract}</IsAbstract>,
                <PeriodType>{$c.PeriodType}</PeriodType>,
                <Balance>{$c.Balance}</Balance>,
                <SubstitutionGroup>{$c.SubstitutionGroup}</SubstitutionGroup>,
                <DataType>{$c.DataType}</DataType>,
                <BaseType>{$c.BaseType}</BaseType>,
                <ClosestSchemaBuiltinType>{$c.ClosestSchemaBuiltinType}</ClosestSchemaBuiltinType>,
                <IsTextBlock>{$c.IsTextBlock}</IsTextBlock>,
                <ComponentRole>{$c.ComponentRole}</ComponentRole>,
                <ComponentLabel>{$c.ComponentLabel}</ComponentLabel>,
                <AccessionNumber>{$c.AccessionNumber}</AccessionNumber>,
                <CIK>{$c.CIK}</CIK>,
                <EntityRegistrantName>{$c.EntityRegistrantName}</EntityRegistrantName>,
                <FiscalYear>{$c.FiscalYear}</FiscalYear>,
                <FiscalPeriod>{$c.FiscalPeriod}</FiscalPeriod>
             )
        }</ReportElement>
};

declare function local:concepts-for-archives($aids as string*) as object*
{
    let $conn :=
      let $credentials := credentials:credentials("MongoDB", "xbrl")
      return
        try {
            mongo:connect($credentials)
        } catch mongo:* {
            error(QName("concepts:CONNECTION-FAILED"), $err:description)
        }
    return
        mongo:find($conn, "concepts",
        {
            "Archive": { "$in" : [ $aids ] }
        })
};

declare function local:concepts-for-archives($aids as string*, $names as string*, $map as string?) as object*
{
    let $conn :=
      let $credentials := credentials:credentials("MongoDB", "xbrl")
      return
        try {
            mongo:connect($credentials)
        } catch mongo:* {
            error(QName("concepts:CONNECTION-FAILED"), $err:description)
        }
    let $map := if (exists($map)) then concept-maps:concept-maps($map) else ()

    let $all-concepts-computable-by-maps as string* := keys($map.Trees)

    let $concepts-computable-by-maps as object* :=
        for $c in $names[$$ = $all-concepts-computable-by-maps]
        return $map.Trees.$c

    let $mapped-names := (keys($concepts-computable-by-maps.To ), $concepts-computable-by-maps.To [].Name)

    let $concepts-not-computable-by-maps as string* := seq:value-except($names, $mapped-names)
    let $results-not-computed-by-maps := mongo:find($conn, "concepts",
        {
            "Name" : { "$in" : [ $concepts-not-computable-by-maps ] },
            "Archive": { "$in" : [ $aids ] }
        })
    let $results-computed-by-maps :=
        for $c in mongo:find($conn, "concepts",
            {
                "Name" : { "$in" : [ $mapped-names ] },
                "Archive": { "$in" : [ $aids ] }
            })
        group by $c.Component
        let $c := $c[1]
        let $map-concept := (for $candidate in $concepts-computable-by-maps
                            where $c.Name = (keys($candidate.To), $candidate.To[].Name)
                            return $candidate)[1]
        return
            copy $n := $c
            modify (
                replace value of json $n.Name with $map-concept.Name,
                insert json  { Origin : $c.Name } into $n)
            return $n
    return ($results-not-computed-by-maps, $results-computed-by-maps)
};

declare function local:concepts-for-archives-and-labels($aids as string*, $labels as string) as object*
{
    let $conn :=
        let $credentials := credentials:credentials("MongoDB", "xbrl")
        return
            try {
                mongo:connect($credentials)
            } catch mongo:* {
                error(QName("components:CONNECTION-FAILED"), $err:description)
            }
    return mongo:run-cmd-deterministic(
        $conn,
        {
            "text" : "concepts",
            "filter" : { "Archive" : { "$in" : [ $aids ] } },
            "search" : $labels,
            "limit" : 100,
            "score" : { "$meta" : "textScore" },
            "sort" : { score: { "$meta" : "textScore" } }
        }).results[].obj
};

session:audit-call();

(: Query parameters :)
let $format as string?         := request:param-values("format")
let $ciks as string*           := distinct-values(request:param-values("cik"))
let $tags as string*           := distinct-values(request:param-values("tag"))
let $tickers as string*        := distinct-values(request:param-values("ticker"))
let $sics as string*           := distinct-values(request:param-values("sic"))
let $fiscalYears as string*    := distinct-values(request:param-values("fiscalYear", "LATEST"))
let $fiscalPeriods as string*  := distinct-values(request:param-values("fiscalPeriod", "FY"))
let $aids as string*           := distinct-values(request:param-values("aid"))
let $labels as string*         := request:param-values("label")
let $map as string?            := request:param-values("map")
let $report as string?         := request:param-values("report")
let $names as string*          := request:param-values("name")

(: Post-processing :)
let $format as string? := (: backwards compatibility, to be deprecated  :)
    lower-case(($format, substring-after(request:path(), ".jq."))[1])
let $tags as string* := (: backwards compatibility, to be deprecated :)
    distinct-values($tags ! upper-case($$))
let $fiscalYears as integer* :=
    for $fy in $fiscalYears ! upper-case($$)
    return switch($fy)
           case "LATEST" return $fiscal-core2:LATEST_FISCAL_YEAR
           case "ALL" return $fiscal-core:ALL_FISCAL_YEARS
           default return if($fy castable as integer) then integer($fy) else ()
let $fiscalPeriods as string* :=
    for $fp in $fiscalPeriods ! upper-case($$)
    return switch($fp)
           case "ALL" return $fiscal-core:ALL_FISCAL_PERIODS
           default return $fp

(: Object resolution :)
let $entities :=
    companies2:companies(
        $ciks,
        $tags,
        $tickers,
        $sics)
let $archives as object* := fiscal-core2:filings(
    $entities,
    $fiscalPeriods,
    $fiscalYears,
    $aids)
let $entities    := entities:entities($archives.Entity)
let $onlyNames   := let $o := request:param-values("onlyNames")[1] return if (exists($o)) then ($o cast as boolean) else false
<<<<<<< HEAD

=======
let $map as item* :=
    if(exists($report))
    then reports:concept-map($report)
    else $map
>>>>>>> ReportParameterInFactsJq
let $concepts := if (exists($names))
                  then local:concepts-for-archives($archives._id, $names, $map)
                  else if (exists($labels))
                  then local:concepts-for-archives-and-labels($archives._id, $labels[1])
                  else local:concepts-for-archives($archives._id)
let $result := {
    ReportElements : [
        if ($onlyNames)
        then distinct-values($concepts.Name)
        else
            let $all-aids := $concepts.Archive
            let $roles := $concepts.Role
            let $components := components2:components-for-archives-and-roles($all-aids, $roles)
            return
            for $concept in $concepts
            group by $archive := $concept.Archive,  $role := $concept.Role
            let $component := $components[$$.Archive eq $archive and $$.Role eq $role]
            let $default-hc := hypercubes:hypercubes-for-components($component, "xbrl:DefaultHypercube")
            let $members := $default-hc.Aspects."xbrl:Concept".Domains."xbrl:ConceptDomain".Members
            let $archive := $archives[$$._id eq $archive]
            let $entity := $entities[$$._id eq $archive.Entity]
            let $metadata := {|
                { Origin : $concept.Origin }[exists($concept.Origin)],
                {
                    Name: $concept.Name,
                    ComponentRole : $component.Role,
                    ComponentLabel : $component.Label,
                    AccessionNumber : $archive._id,
                    CIK : $entity._id,
                    EntityRegistrantName : $entity.Profiles.SEC.CompanyName,
                    FiscalYear : $archive.Profiles.SEC.Fiscal.DocumentFiscalYearFocus,
                    FiscalPeriod : $archive.Profiles.SEC.Fiscal.DocumentFiscalPeriodFocus
                }
            |}
            for $concept in $concept
            let $original-name := ($concept.Origin, $concept.Name)[1]
            return {|
                trim($members.$original-name, "Name"),
                $metadata
            |}
    ]
}
let $comment := {
    NumConcepts: count($concepts),
    TotalNumConcepts: session:num-concepts()
}
let $serializers := {
    to-xml : function($res as object) as node() {
        <ReportElements>{
                    local:to-xml($res.ReportElements[], $onlyNames)
                }</ReportElements>
    },
    to-csv : function($res as object) as string {
        local:to-csv($res.ReportElements[], $onlyNames)
    }
}
let $results := util:serialize($result, $comment, $serializers, $format, "report-elements")
return
    util:check-and-return-results($entities, $results, $format)


:green_heart:

import module namespace test = "http://apps.28.io/test";

test:check-all-success({
    bs-one: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyConsolidatedBalanceSheet",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-one.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    bs-one-en: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyConsolidatedBalanceSheet",
        language: "en",
        eliminate: "true"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-one-en.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    bs-one-default: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyConsolidatedBalanceSheet",
        eliminate: "true"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-one-en.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    pl-one: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_YearToQuarterEndConsolidatedStatementOfIncome",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-pl-one.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    cf-one: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jpcrp/rol_NotesQuarterlyConsolidatedStatementOfCashFlows",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-cf-one.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    bs-companies: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        tag: "NIKKEI",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyConsolidatedBalanceSheet",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-companies.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    pl-companies: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        tag: "NIKKEI",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_YearToQuarterEndConsolidatedStatementOfIncome",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-pl-companies.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    cf-companies: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        tag: "NIKKEI",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jpcrp/rol_NotesQuarterlyConsolidatedStatementOfCashFlows",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-cf-companies.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    bs-periods: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://info.edinet-fsa.go.jp E01225-000",
        fiscalYear: "ALL",
        fiscalPeriod: "FY",
        filingKind: "EDINET",
        role: "http://info.edinet-fsa.go.jp/jp/fr/gaap/role/ConsolidatedBalanceSheets",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-periods.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    pl-periods: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://info.edinet-fsa.go.jp E01225-000",
        fiscalYear: "ALL",
        fiscalPeriod: "FY",
        filingKind: "EDINET",
        role: "http://info.edinet-fsa.go.jp/jp/fr/gaap/role/ConsolidatedStatementsOfIncome",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-pl-periods.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    ),
    cf-periods: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://info.edinet-fsa.go.jp E01225-000",
        fiscalYear: "ALL",
        fiscalPeriod: "FY",
        filingKind: "EDINET",
        role: "http://info.edinet-fsa.go.jp/jp/fr/gaap/role/ConsolidatedStatementsOfCashFlowsIndirect",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-cf-periods.jq"),
      { TrimIdField: true, NoArrayOrder: true }
    )
})

import module namespace test = "http://apps.28.io/test";

test:check-all-success({
    bs-one: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyConsolidatedBalanceSheet",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-one.jq"),
      { TrimIdField: true }
    ),
    bs-one-en: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyConsolidatedBalanceSheet",
        language: "en",
        eliminate: "true"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-one-en.jq"),
      { TrimIdField: true }
    ),
    bs-one-default: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyConsolidatedBalanceSheet",
        eliminate: "true"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-one-en.jq"),
      { TrimIdField: true }
    ),
    pl-one: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_YearToQuarterEndConsolidatedStatementOfIncome",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-pl-one.jq"),
      { TrimIdField: true }
    ),
    cf-one: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://disclosure.edinet-fsa.go.jp E01225-000",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jpcrp/rol_NotesQuarterlyConsolidatedStatementOfCashFlows",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-cf-one.jq"),
      { TrimIdField: true }
    ),
    bs-companies: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        tag: "NIKKEI",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyConsolidatedBalanceSheet",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-companies.jq"),
      { TrimIdField: true }
    ),
    pl-companies: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        tag: "NIKKEI",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_YearToQuarterEndConsolidatedStatementOfIncome",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-pl-companies.jq"),
      { TrimIdField: true }
    ),
    cf-companies: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        tag: "NIKKEI",
        fiscalYear: "2014",
        fiscalPeriod: "Q1",
        filingKind: "EDINET",
        role: "http://disclosure.edinet-fsa.go.jp/role/jpcrp/rol_NotesQuarterlyConsolidatedStatementOfCashFlows",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-cf-companies.jq"),
      { TrimIdField: true }
    ),
    bs-periods: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://info.edinet-fsa.go.jp E01225-000",
        fiscalYear: "ALL",
        fiscalPeriod: "FY",
        filingKind: "EDINET",
        role: "http://info.edinet-fsa.go.jp/jp/fr/gaap/role/ConsolidatedBalanceSheets",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-bs-periods.jq"),
      { TrimIdField: true }
    ),
    pl-periods: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://info.edinet-fsa.go.jp E01225-000",
        fiscalYear: "ALL",
        fiscalPeriod: "FY",
        filingKind: "EDINET",
        role: "http://info.edinet-fsa.go.jp/jp/fr/gaap/role/ConsolidatedStatementsOfIncome",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-pl-periods.jq"),
      { TrimIdField: true }
    ),
    cf-periods: test:invoke-and-assert-deep-equal(
      "spreadsheet-for-component",
      {
        eid: "http://info.edinet-fsa.go.jp E01225-000",
        fiscalYear: "ALL",
        fiscalPeriod: "FY",
        filingKind: "EDINET",
        role: "http://info.edinet-fsa.go.jp/jp/fr/gaap/role/ConsolidatedStatementsOfCashFlowsIndirect",
        eliminate: "true",
        language: "ja"
      },
      function($b as item*) as item* { $b },
      test:get-expected-result("edinet/spreadsheet-for-component-expected-cf-periods.jq"),
      { TrimIdField: true }
    )
})