예제 #1
0
 def checkHierarchyConstraints(elt):
     constraints = ixHierarchyConstraints.get(elt.localName)
     if constraints:
         for _rel, names in constraints:
             reqt = _rel[0]
             rel = _rel[1:]
             if reqt in ('&', '^', '1'):
                 nameFilter = ('*', )
             else:
                 nameFilter = names
             if nameFilter == ('*', ):
                 namespaceFilter = namespacePrefix = '*'
             elif len(nameFilter) == 1 and "}" in nameFilter[
                     0] and nameFilter[0][0] == "{":
                 namespaceFilter, _sep, nameFilter = nameFilter[0][
                     1:].partition("}")
                 namespacePrefix = XmlUtil.xmlnsprefix(elt, namespaceFilter)
             else:
                 namespaceFilter = elt.namespaceURI
                 namespacePrefix = elt.prefix
             relations = {
                 "ancestor": XmlUtil.ancestor,
                 "parent": XmlUtil.parent,
                 "child-choice": XmlUtil.children,
                 "child-sequence": XmlUtil.children,
                 "child-or-text": XmlUtil.children,
                 "descendant": XmlUtil.descendants
             }[rel](elt, namespaceFilter, nameFilter)
             if rel in ("ancestor", "parent"):
                 if relations is None: relations = []
                 else: relations = [relations]
             if rel == "child-or-text":
                 relations += XmlUtil.innerTextNodes(elt,
                                                     ixExclude=True,
                                                     ixEscape=False,
                                                     ixContinuation=False)
             issue = ''
             if reqt in ('^', ):
                 if not any(r.localName in names
                            and r.namespaceURI == elt.namespaceURI
                            for r in relations):
                     issue = " and is missing one of " + ', '.join(names)
             if reqt in ('1', ) and not elt.isNil:
                 if sum(r.localName in names
                        and r.namespaceURI == elt.namespaceURI
                        for r in relations) != 1:
                     issue = " and must have exactly one of " + ', '.join(
                         names)
             if reqt in ('&', '^'):
                 disallowed = [
                     str(r.elementQname) for r in relations
                     if not (r.tag in names or
                             (r.localName in names
                              and r.namespaceURI == elt.namespaceURI))
                 ]
                 if disallowed:
                     issue += " and may not have " + ", ".join(disallowed)
                 elif rel == "child-sequence":
                     sequencePosition = 0
                     for i, r in enumerate(relations):
                         rPos = names.index(str(r.localName))
                         if rPos < sequencePosition:
                             issue += " and is out of sequence: " + str(
                                 r.elementQname)
                         else:
                             sequencePosition = rPos
             if reqt == '?' and len(relations) > 1:
                 issue = " may only have 0 or 1 but {0} present ".format(
                     len(relations))
             if reqt == '+' and len(relations) == 0:
                 issue = " must have at least 1 but none present "
             disallowedChildText = bool(
                 reqt == '&' and rel in ("child-sequence", "child-choice")
                 and elt.textValue.strip())
             if ((reqt == '+' and not relations)
                     or (reqt == '-' and relations) or (issue)
                     or disallowedChildText):
                 code = "{}:{}".format(
                     ixSect[elt.namespaceURI].get(elt.localName,
                                                  "other")["constraint"],
                     {
                         'ancestor': "ancestorNode",
                         'parent': "parentNode",
                         'child-choice': "childNodes",
                         'child-sequence': "childNodes",
                         'child-or-text': "childNodesOrText",
                         'descendant': "descendantNodes"
                     }[rel] + {
                         '+': "Required",
                         '-': "Disallowed",
                         '&': "Allowed",
                         '^': "Specified",
                         '1': "Specified"
                     }.get(reqt, "Specified"))
                 msg = _("Inline XBRL ix:{0} {1} {2} {3} {4} element{5}"
                         ).format(
                             elt.localName, {
                                 '+': "must",
                                 '-': "may not",
                                 '&': "may only",
                                 '?': "may",
                                 '+': "must",
                                 '^': "must",
                                 '1': "must"
                             }[reqt], {
                                 'ancestor': "be nested in",
                                 'parent': "have parent",
                                 'child-choice': "have child",
                                 'child-sequence': "have child",
                                 'child-or-text': "have child or text,",
                                 'descendant': "have as descendant"
                             }[rel],
                             '' if rel == 'child-or-text' else ', '.join(
                                 str(r.elementQname) for r in relations) if
                             names == ('*', ) and relations else ", ".join(
                                 "{}:{}".format(namespacePrefix, n)
                                 for n in names), issue,
                             " and no child text (\"{}\")".format(
                                 elt.textValue.strip()[:32])
                             if disallowedChildText else "")
                 modelXbrl.error(
                     code,
                     msg,
                     modelObject=[elt] + relations,
                     requirement=reqt,
                     messageCodes=
                     ("ix{ver.sect}:ancestorNode{Required|Disallowed}",
                      "ix{ver.sect}:childNodesOrTextRequired",
                      "ix{ver.sect}:childNodes{Required|Disallowed|Allowed}",
                      "ix{ver.sect}:descendantNodesDisallowed",
                      "ix{ver.sect}:parentNodeRequired"))
     # other static element checks (that don't require a complete object model, context, units, etc
     if elt.localName == "nonFraction":
         childElts = XmlUtil.children(elt, '*', '*')
         hasText = (elt.text or "") or any(
             (childElt.tail or "") for childElt in childElts)
         if elt.isNil:
             ancestorNonFractions = XmlUtil.ancestors(
                 elt, _ixNS, elt.localName)
             if ancestorNonFractions:
                 modelXbrl.error(
                     ixMsgCode("nonFractionAncestors", elt),
                     _("Fact %(fact)s is a nil nonFraction and MUST not have an ancestor ix:nonFraction"
                       ),
                     modelObject=[elt] + ancestorNonFractions,
                     fact=elt.qname)
             if childElts or hasText:
                 modelXbrl.error(
                     ixMsgCode("nonFractionTextAndElementChildren", elt),
                     _("Fact %(fact)s is a nil nonFraction and MUST not have an child elements or text"
                       ),
                     modelObject=[elt] + childElts,
                     fact=elt.qname)
                 elt.setInvalid(
                 )  # prevent further validation or cascading errors
         else:
             if ((childElts and
                  (len(childElts) != 1 or childElts[0].namespaceURI != _ixNS
                   or childElts[0].localName != "nonFraction"))
                     or (childElts and hasText)):
                 modelXbrl.error(
                     ixMsgCode("nonFractionTextAndElementChildren", elt),
                     _("Fact %(fact)s is a non-nil nonFraction and MUST have exactly one ix:nonFraction child element or text."
                       ),
                     modelObject=[elt] + childElts,
                     fact=elt.qname)
                 elt.setInvalid()
     if elt.localName == "fraction":
         if elt.isNil:
             ancestorFractions = XmlUtil.ancestors(elt, _ixNS,
                                                   elt.localName)
             if ancestorFractions:
                 modelXbrl.error(
                     ixMsgCode("fractionAncestors", elt),
                     _("Fact %(fact)s is a nil fraction and MUST not have an ancestor ix:fraction"
                       ),
                     modelObject=[elt] + ancestorFractions,
                     fact=elt.qname)
         else:
             nonFrChildren = [
                 e for e in XmlUtil.children(elt, _ixNS, '*')
                 if e.localName not in ("fraction", "numerator",
                                        "denominator")
             ]
             if nonFrChildren:
                 modelXbrl.error(
                     ixMsgCode("fractionElementChildren", elt),
                     _("Fact %(fact)s is a non-nil fraction and not have any child elements except ix:fraction, ix:numerator and ix:denominator: %(children)s"
                       ),
                     modelObject=[elt] + nonFrChildren,
                     fact=elt.qname,
                     children=", ".join(e.localName for e in nonFrChildren))
             for ancestorFraction in XmlUtil.ancestors(
                     elt, XbrlConst.ixbrl11, "fraction"):  # only ix 1.1
                 if normalizeSpace(elt.get("unitRef")) != normalizeSpace(
                         ancestorFraction.get("unitRef")):
                     modelXbrl.error(
                         ixMsgCode("fractionNestedUnitRef", elt),
                         _("Fact %(fact)s fraction and ancestor fractions must have matching unitRefs: %(unitRef)s, %(unitRef2)s"
                           ),
                         modelObject=[elt] + nonFrChildren,
                         fact=elt.qname,
                         unitRef=elt.get("unitRef"),
                         unitRef2=ancestorFraction.get("unitRef"))
     if elt.localName in ("nonFraction", "numerator", "denominator",
                          "nonNumeric"):
         fmt = elt.format
         if fmt:
             if fmt in _customTransforms:
                 pass
             elif fmt.namespaceURI not in FunctionIxt.ixtNamespaceFunctions:
                 modelXbrl.error(
                     ixMsgCode("invalidTransformation",
                               elt,
                               sect="validation"),
                     _("Fact %(fact)s has unrecognized transformation namespace %(namespace)s"
                       ),
                     modelObject=elt,
                     fact=elt.qname,
                     transform=fmt,
                     namespace=fmt.namespaceURI)
                 elt.setInvalid()
             elif fmt.localName not in FunctionIxt.ixtNamespaceFunctions[
                     fmt.namespaceURI]:
                 modelXbrl.error(
                     ixMsgCode("invalidTransformation",
                               elt,
                               sect="validation"),
                     _("Fact %(fact)s has unrecognized transformation name %(name)s"
                       ),
                     modelObject=elt,
                     fact=elt.qname,
                     transform=fmt,
                     name=fmt.localName)
                 elt.setInvalid()
예제 #2
0
 def checkHierarchyConstraints(elt):
     constraints = ixHierarchyConstraints.get(elt.localName)
     if constraints:
         for _rel, names in constraints:
             reqt = _rel[0]
             rel = _rel[1:]
             if reqt in ('&', '^', '1'):
                 nameFilter = ('*',)
             else:
                 nameFilter = names
             if nameFilter == ('*',):
                 namespaceFilter = namespacePrefix = '*'
             elif len(nameFilter) == 1 and "}" in nameFilter[0] and nameFilter[0][0] == "{":
                 namespaceFilter, _sep, nameFilter = nameFilter[0][1:].partition("}")
                 namespacePrefix = XmlUtil.xmlnsprefix(elt,namespaceFilter)
             else:
                 namespaceFilter = elt.namespaceURI
                 namespacePrefix = elt.prefix
             relations = {"ancestor": XmlUtil.ancestor, 
                          "parent": XmlUtil.parent, 
                          "child-choice": XmlUtil.children, 
                          "child-sequence": XmlUtil.children,
                          "child-or-text": XmlUtil.children,
                          "descendant": XmlUtil.descendants}[rel](
                         elt, 
                         namespaceFilter,
                         nameFilter)
             if rel in ("ancestor", "parent"):
                 if relations is None: relations = []
                 else: relations = [relations]
             if rel == "child-or-text":
                 relations += XmlUtil.innerTextNodes(elt, ixExclude=True, ixEscape=False, ixContinuation=False, ixResolveUris=False)
             issue = ''
             if reqt in ('^',):
                 if not any(r.localName in names and r.namespaceURI == elt.namespaceURI
                            for r in relations):
                     issue = " and is missing one of " + ', '.join(names)
             if reqt in ('1',) and not elt.isNil:
                 if sum(r.localName in names and r.namespaceURI == elt.namespaceURI
                        for r in relations) != 1:
                     issue = " and must have exactly one of " + ', '.join(names)
             if reqt in ('&', '^'):
                 disallowed = [str(r.elementQname)
                               for r in relations
                               if not (r.tag in names or
                                       (r.localName in names and r.namespaceURI == elt.namespaceURI))]
                 if disallowed:
                     issue += " and may not have " + ", ".join(disallowed)
                 elif rel == "child-sequence":
                     sequencePosition = 0
                     for i, r in enumerate(relations):
                         rPos = names.index(str(r.localName))
                         if rPos < sequencePosition:
                             issue += " and is out of sequence: " + str(r.elementQname)
                         else:
                             sequencePosition = rPos
             if reqt == '?' and len(relations) > 1:
                 issue = " may only have 0 or 1 but {0} present ".format(len(relations))
             if reqt == '+' and len(relations) == 0:
                 issue = " must have at least 1 but none present "
             disallowedChildText = bool(reqt == '&' and 
                                        rel in ("child-sequence", "child-choice") 
                                        and elt.textValue.strip())
             if ((reqt == '+' and not relations) or
                 (reqt == '-' and relations) or
                 (issue) or disallowedChildText):
                 code = "{}:{}".format(ixSect[elt.namespaceURI].get(elt.localName,"other")["constraint"], {
                        'ancestor': "ancestorNode",
                        'parent': "parentNode",
                        'child-choice': "childNodes",
                        'child-sequence': "childNodes",
                        'child-or-text': "childNodesOrText",
                        'descendant': "descendantNodes"}[rel] + {
                         '+': "Required",
                         '-': "Disallowed",
                         '&': "Allowed",
                         '^': "Specified",
                         '1': "Specified"}.get(reqt, "Specified"))
                 msg = _("Inline XBRL ix:{0} {1} {2} {3} {4} element{5}").format(
                             elt.localName,
                             {'+': "must", '-': "may not", '&': "may only",
                              '?': "may", '+': "must", '^': "must", '1': "must"}[reqt],
                             {'ancestor': "be nested in",
                              'parent': "have parent",
                              'child-choice': "have child",
                              'child-sequence': "have child",
                              'child-or-text': "have child or text,",
                              'descendant': "have as descendant"}[rel],
                             '' if rel == 'child-or-text' else
                             ', '.join(str(r.elementQname) for r in relations)
                             if names == ('*',) and relations else
                             ", ".join("{}:{}".format(namespacePrefix, n) for n in names),
                             issue,
                             " and no child text (\"{}\")".format(elt.textValue.strip()[:32]) if disallowedChildText else "")
                 modelXbrl.error(code, msg, 
                                 modelObject=[elt] + relations, requirement=reqt,
                                 messageCodes=("ix{ver.sect}:ancestorNode{Required|Disallowed}",
                                               "ix{ver.sect}:childNodesOrTextRequired",
                                               "ix{ver.sect}:childNodes{Required|Disallowed|Allowed}",
                                               "ix{ver.sect}:descendantNodesDisallowed",
                                               "ix{ver.sect}:parentNodeRequired"))
     # other static element checks (that don't require a complete object model, context, units, etc
     if elt.localName == "nonFraction":
         childElts = XmlUtil.children(elt, '*', '*')
         hasText = (elt.text or "") or any((childElt.tail or "") for childElt in childElts)
         if elt.isNil:
             ancestorNonFractions = XmlUtil.ancestors(elt, _ixNS, elt.localName)
             if ancestorNonFractions:
                 modelXbrl.error(ixMsgCode("nonFractionAncestors", elt),
                     _("Fact %(fact)s is a nil nonFraction and MUST not have an ancestor ix:nonFraction"),
                     modelObject=[elt] + ancestorNonFractions, fact=elt.qname)
             if childElts or hasText:
                 modelXbrl.error(ixMsgCode("nonFractionTextAndElementChildren", elt),
                     _("Fact %(fact)s is a nil nonFraction and MUST not have an child elements or text"),
                     modelObject=[elt] + childElts, fact=elt.qname)
                 elt.setInvalid() # prevent further validation or cascading errors
         else:
             if ((childElts and (len(childElts) != 1 or childElts[0].namespaceURI != _ixNS or childElts[0].localName != "nonFraction")) or
                 (childElts and hasText)):
                 modelXbrl.error(ixMsgCode("nonFractionTextAndElementChildren", elt),
                     _("Fact %(fact)s is a non-nil nonFraction and MUST have exactly one ix:nonFraction child element or text."),
                     modelObject=[elt] + childElts, fact=elt.qname)
                 elt.setInvalid()
     if elt.localName == "fraction":
         if elt.isNil:
             ancestorFractions = XmlUtil.ancestors(elt, _ixNS, elt.localName)
             if ancestorFractions:
                 modelXbrl.error(ixMsgCode("fractionAncestors", elt),
                     _("Fact %(fact)s is a nil fraction and MUST not have an ancestor ix:fraction"),
                     modelObject=[elt] + ancestorFractions, fact=elt.qname)
         else:
             nonFrChildren = [e for e in XmlUtil.children(elt, _ixNS, '*') if e.localName not in ("fraction", "numerator", "denominator")]
             if nonFrChildren:
                 modelXbrl.error(ixMsgCode("fractionElementChildren", elt),
                     _("Fact %(fact)s is a non-nil fraction and not have any child elements except ix:fraction, ix:numerator and ix:denominator: %(children)s"),
                     modelObject=[elt] + nonFrChildren, fact=elt.qname, children=", ".join(e.localName for e in nonFrChildren))
             for ancestorFraction in XmlUtil.ancestors(elt, XbrlConst.ixbrl11, "fraction"): # only ix 1.1
                 if normalizeSpace(elt.get("unitRef")) != normalizeSpace(ancestorFraction.get("unitRef")):
                     modelXbrl.error(ixMsgCode("fractionNestedUnitRef", elt),
                         _("Fact %(fact)s fraction and ancestor fractions must have matching unitRefs: %(unitRef)s, %(unitRef2)s"),
                         modelObject=[elt] + nonFrChildren, fact=elt.qname, unitRef=elt.get("unitRef"), unitRef2=ancestorFraction.get("unitRef"))
     if elt.localName in ("nonFraction", "numerator", "denominator", "nonNumeric"):
         fmt = elt.format
         if fmt:
             if fmt in _customTransforms:
                 pass
             elif fmt.namespaceURI not in FunctionIxt.ixtNamespaceFunctions:
                 modelXbrl.error(ixMsgCode("invalidTransformation", elt, sect="validation"),
                     _("Fact %(fact)s has unrecognized transformation namespace %(namespace)s"),
                     modelObject=elt, fact=elt.qname, transform=fmt, namespace=fmt.namespaceURI)
                 elt.setInvalid()
             elif fmt.localName not in FunctionIxt.ixtNamespaceFunctions[fmt.namespaceURI]:
                 modelXbrl.error(ixMsgCode("invalidTransformation", elt, sect="validation"),
                     _("Fact %(fact)s has unrecognized transformation name %(name)s"),
                     modelObject=elt, fact=elt.qname, transform=fmt, name=fmt.localName)
                 elt.setInvalid()