示例#1
0
def equalityHash(elt, equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED):
    if isinstance(elt, ModelObject):
        try:
            if equalMode == S_EQUAL:
                return elt._hashSEqual
            else:
                return elt._hashXpathEqual
        except AttributeError:
            dts = elt.modelXbrl
            if not hasattr(elt,"xValid"):
                xmlValidate(dts, elt)
            hashableValue = elt.sValue if equalMode == S_EQUAL else elt.xValue
            if isinstance(hashableValue,float) and math.isnan(hashableValue): 
                hashableValue = (hashableValue,elt)    # ensure this NaN only compares to itself and no other NaN
            _hash = hash((elt.elementQname,
                          hashableValue,
                          tuple(sorted(attributeDict(dts, elt, (), equalMode, excludeIDs, distinguishNaNs=True).items(),
                                       key=lambda item: item[0])), # must sort so attrs always hashed in same order
                          tuple(equalityHash(child,equalMode,excludeIDs) for child in childElements(elt))
                          ))
            if equalMode == S_EQUAL:
                elt._hashSEqual = _hash
            else:
                elt._hashXpathEqual = _hash
            return _hash
    elif isinstance(elt, (tuple,list,set)):
        return hash( tuple(equalityHash(i) for i in elt) )
    else:
        return hash(None)
示例#2
0
def sEqual(dts1, elt1, elt2, equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED, dts2=None, ns2ns1Tbl=None):
    if dts2 is None: dts2 = dts1
    if elt1.localName != elt2.localName:
        return False
    if ns2ns1Tbl and elt2.namespaceURI in ns2ns1Tbl:
        if elt1.namespaceURI != ns2ns1Tbl[elt2.namespaceURI]:
            return False
    elif elt1.namespaceURI != elt2.namespaceURI:
        return False
    if not hasattr(elt1,"xValid"):
        xmlValidate(dts1, elt1)
    if not hasattr(elt2,"xValid"):
        xmlValidate(dts2, elt2)
    children1 = childElements(elt1)
    children2 = childElements(elt2)
    if len(children1) != len(children2):
        return False
    if (not xEqual(elt1, elt2,
                   # must use stringValue for nested contents of mixed content 
                   # ... this is now in xValue for mixed content
                   # VALIDATE_BY_STRING_VALUE if len(children1) and elt1.xValid == VALID else
                   equalMode
                   ) or 
        attributeDict(dts1, elt1, (), equalMode, excludeIDs) != 
        attributeDict(dts2, elt2, (), equalMode, excludeIDs, ns2ns1Tbl)):
        return False
    excludeChildIDs = excludeIDs if excludeIDs != TOP_IDs_EXCLUDED else NO_IDs_EXCLUDED
    for i in range( len(children1) ):
        if not sEqual(dts1, children1[i], children2[i], equalMode, excludeChildIDs, dts2, ns2ns1Tbl):
            return False
    return True
示例#3
0
def attributeDict(modelXbrl, elt, exclusions=set(), equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED, ns2ns1Tbl=None, keyByTag=False, distinguishNaNs=False):
    if not hasattr(elt,"xValid"):
        xmlValidate(modelXbrl, elt)
    attrs = {}
    # TBD: replace with validated attributes
    for modelAttribute in elt.xAttributes.values():
        attrTag = modelAttribute.attrTag
        ns, sep, localName = attrTag.partition('}')
        attrNsURI = ns[1:] if sep else None
        if ns2ns1Tbl and attrNsURI in ns2ns1Tbl:
            attrNsURI = ns2ns1Tbl[attrNsURI]
        if (attrTag not in exclusions and 
            (attrNsURI is None or attrNsURI not in exclusions)):
            if keyByTag:
                qname = attrTag
            elif attrNsURI is not None:
                qname = QName(None, attrNsURI, localName)
            else:
                qname = QName(None, None, attrTag)
            try:
                if excludeIDs and getattr(modelAttribute, "xValid", 0) == VALID_ID:
                    continue
                if modelAttribute.xValid != UNKNOWN:
                    value = modelAttribute.sValue if equalMode <= S_EQUAL2 else modelAttribute.xValue
                else: # unable to validate, no schema definition, use string value of attribute
                    value = modelAttribute.text
                if distinguishNaNs and isinstance(value,float) and math.isnan(value):
                    value = (value,elt)
                attrs[qname] = value
            except KeyError:
                pass  # what should be done if attribute failed to have psvi value
    return attrs
示例#4
0
def xEqual(elt1, elt2, equalMode=S_EQUAL):
    if not hasattr(elt1,"xValid"):
        xmlValidate(elt1.modelXbrl, elt1)
    if not hasattr(elt2,"xValid"):
        xmlValidate(elt2.modelXbrl, elt2)
    if equalMode == VALIDATE_BY_STRING_VALUE:
        return elt1.stringValue == elt2.stringValue
    elif equalMode == S_EQUAL or (equalMode == S_EQUAL2 and not isinstance(elt1.sValue, QName)):
        return elt1.sValue == elt2.sValue
    else:
        return elt1.xValue == elt2.xValue
示例#5
0
def xEqual(elt1, elt2, equalMode=S_EQUAL):
    if not hasattr(elt1, "xValid"):
        xmlValidate(elt1.modelXbrl, elt1)
    if not hasattr(elt2, "xValid"):
        xmlValidate(elt2.modelXbrl, elt2)
    if equalMode == VALIDATE_BY_STRING_VALUE:
        return elt1.stringValue == elt2.stringValue
    elif equalMode == S_EQUAL:  # formula WG e-mail 2018-09-06: or (equalMode == S_EQUAL2 and not isinstance(elt1.sValue, QName)):
        return elt1.sValue == elt2.sValue
    else:  # includes dimension S-equal2, use xpath-2 equality.
        return elt1.xValue == elt2.xValue
示例#6
0
文件: XbrlUtil.py 项目: Arelle/Arelle
def xEqual(elt1, elt2, equalMode=S_EQUAL):
    if not hasattr(elt1,"xValid"):
        xmlValidate(elt1.modelXbrl, elt1)
    if not hasattr(elt2,"xValid"):
        xmlValidate(elt2.modelXbrl, elt2)
    if equalMode == VALIDATE_BY_STRING_VALUE:
        return elt1.stringValue == elt2.stringValue
    elif equalMode == S_EQUAL: # formula WG e-mail 2018-09-06: or (equalMode == S_EQUAL2 and not isinstance(elt1.sValue, QName)):
        return elt1.sValue == elt2.sValue
    else: # includes dimension S-equal2, use xpath-2 equality.
        return elt1.xValue == elt2.xValue
示例#7
0
def xEqual(elt1, elt2, equalMode=S_EQUAL):
    if not hasattr(elt1, "xValid"):
        xmlValidate(elt1.modelXbrl, elt1)
    if not hasattr(elt2, "xValid"):
        xmlValidate(elt2.modelXbrl, elt2)
    if equalMode == VALIDATE_BY_STRING_VALUE:
        return elt1.stringValue == elt2.stringValue
    elif equalMode == S_EQUAL or (equalMode == S_EQUAL2
                                  and not isinstance(elt1.sValue, QName)):
        return elt1.sValue == elt2.sValue
    else:
        return elt1.xValue == elt2.xValue
示例#8
0
def typedValue(dts, element, attrQname=None):
    try:
        if attrQname: # PSVI attribute value
            modelAttribute = element.xAttributes[attrQname.clarkNotation]
            if modelAttribute.xValid >= VALID:
                return modelAttribute.xValue
        else: # PSVI element value (of text)
            if element.xValid >= VALID:
                return element.xValue
    except (AttributeError, KeyError):
        if dts:
            xmlValidate(dts, element, recurse=False, attrQname=attrQname)
            return typedValue(None, element, attrQname=attrQname)
    return None
示例#9
0
def typedValue(dts, element, attrQname=None):
    try:
        if attrQname:  # PSVI attribute value
            modelAttribute = element.xAttributes[attrQname.clarkNotation]
            if modelAttribute.xValid >= VALID:
                return modelAttribute.xValue
        else:  # PSVI element value (of text)
            if element.xValid >= VALID:
                return element.xValue
    except (AttributeError, KeyError):
        if dts:
            xmlValidate(dts, element, recurse=False, attrQname=attrQname)
            return typedValue(None, element, attrQname=attrQname)
    return None
示例#10
0
def attributeDict(modelXbrl,
                  elt,
                  exclusions=set(),
                  equalMode=S_EQUAL,
                  excludeIDs=NO_IDs_EXCLUDED,
                  ns2ns1Tbl=None,
                  keyByTag=False,
                  distinguishNaNs=False):
    if not hasattr(elt, "xValid"):
        xmlValidate(modelXbrl, elt)
    attrs = {}
    # TBD: replace with validated attributes
    for modelAttribute in elt.xAttributes.values():
        attrTag = modelAttribute.attrTag
        ns, sep, localName = attrTag.partition('}')
        attrNsURI = ns[1:] if sep else None
        if ns2ns1Tbl and attrNsURI in ns2ns1Tbl:
            attrNsURI = ns2ns1Tbl[attrNsURI]
        if (attrTag not in exclusions
                and (attrNsURI is None or attrNsURI not in exclusions)):
            if keyByTag:
                qname = attrTag
            elif attrNsURI is not None:
                qname = QName(None, attrNsURI, localName)
            else:
                qname = QName(None, None, attrTag)
            try:
                if excludeIDs and getattr(modelAttribute, "xValid",
                                          0) == VALID_ID:
                    continue
                if modelAttribute.xValid != UNKNOWN:
                    value = modelAttribute.sValue if equalMode <= S_EQUAL2 else modelAttribute.xValue
                else:  # unable to validate, no schema definition, use string value of attribute
                    value = modelAttribute.text
                if distinguishNaNs and isinstance(value,
                                                  float) and math.isnan(value):
                    value = (value, elt)
                attrs[qname] = value
            except KeyError:
                pass  # what should be done if attribute failed to have psvi value
    return attrs
示例#11
0
def sEqual(dts1,
           elt1,
           elt2,
           equalMode=S_EQUAL,
           excludeIDs=NO_IDs_EXCLUDED,
           dts2=None,
           ns2ns1Tbl=None):
    if dts2 is None: dts2 = dts1
    if elt1.localName != elt2.localName:
        return False
    if ns2ns1Tbl and elt2.namespaceURI in ns2ns1Tbl:
        if elt1.namespaceURI != ns2ns1Tbl[elt2.namespaceURI]:
            return False
    elif elt1.namespaceURI != elt2.namespaceURI:
        return False
    if not hasattr(elt1, "xValid"):
        xmlValidate(dts1, elt1)
    if not hasattr(elt2, "xValid"):
        xmlValidate(dts2, elt2)
    children1 = childElements(elt1)
    children2 = childElements(elt2)
    if len(children1) != len(children2):
        return False
    if (not xEqual(
            elt1,
            elt2,
            # must use stringValue for nested contents of mixed content
            # ... this is now in xValue for mixed content
            # VALIDATE_BY_STRING_VALUE if len(children1) and elt1.xValid == VALID else
            equalMode) or attributeDict(dts1, elt1, (), equalMode, excludeIDs)
            != attributeDict(dts2, elt2,
                             (), equalMode, excludeIDs, ns2ns1Tbl)):
        return False
    excludeChildIDs = excludeIDs if excludeIDs != TOP_IDs_EXCLUDED else NO_IDs_EXCLUDED
    for i in range(len(children1)):
        if not sEqual(dts1, children1[i], children2[i], equalMode,
                      excludeChildIDs, dts2, ns2ns1Tbl):
            return False
    return True
示例#12
0
def equalityHash(elt, equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED):
    if isinstance(elt, ModelObject):
        try:
            if equalMode == S_EQUAL:
                return elt._hashSEqual
            else:
                return elt._hashXpathEqual
        except AttributeError:
            dts = elt.modelXbrl
            if not hasattr(elt, "xValid"):
                xmlValidate(dts, elt)
            hashableValue = elt.sValue if equalMode == S_EQUAL else elt.xValue
            if isinstance(hashableValue, float) and math.isnan(hashableValue):
                hashableValue = (
                    hashableValue, elt
                )  # ensure this NaN only compares to itself and no other NaN
            _hash = hash((
                elt.elementQname,
                hashableValue,
                tuple(
                    sorted(attributeDict(dts,
                                         elt, (),
                                         equalMode,
                                         excludeIDs,
                                         distinguishNaNs=True).items(),
                           key=lambda item: item[0])
                ),  # must sort so attrs always hashed in same order
                tuple(
                    equalityHash(child, equalMode, excludeIDs)
                    for child in childElements(elt))))
            if equalMode == S_EQUAL:
                elt._hashSEqual = _hash
            else:
                elt._hashXpathEqual = _hash
            return _hash
    elif isinstance(elt, (tuple, list, set)):
        return hash(tuple(equalityHash(i) for i in elt))
    else:
        return hash(None)
示例#13
0
def vEqual(elt1, elt2):
    if not hasattr(elt1,"xValid"):
        xmlValidate(elt1.modelXbrl, elt1)
    if not hasattr(elt2,"xValid"):
        xmlValidate(elt2.modelXbrl, elt2)
    return elt1.sValue == elt2.sValue
示例#14
0
def vEqual(elt1, elt2):
    if not hasattr(elt1, "xValid"):
        xmlValidate(elt1.modelXbrl, elt1)
    if not hasattr(elt2, "xValid"):
        xmlValidate(elt2.modelXbrl, elt2)
    return elt1.sValue == elt2.sValue
示例#15
0
 def stepAxis(self, op, p, sourceSequence):
     targetSequence = []
     for node in sourceSequence:
         if not isinstance(node,(ModelObject, etree._ElementTree, ModelAttribute)):
             raise XPathException(self.progHeader, 'err:XPTY0020', _('Axis step {0} context item is not a node: {1}').format(op, node))
         targetNodes = []
         if isinstance(p,QNameDef):
             ns = p.namespaceURI; localname = p.localName; axis = p.axis
             if p.isAttribute:
                 if isinstance(node,ModelObject):
                     attrTag = p.localName if p.unprefixed else p.clarkNotation
                     modelAttribute = None
                     try:
                         modelAttribute = node.xAttributes[attrTag]
                     except (AttributeError, TypeError, IndexError, KeyError):
                         # may be lax or deferred validated
                         try:
                             xmlValidate(node.modelXbrl, node, p)
                             modelAttribute = node.xAttributes[attrTag]
                         except (AttributeError, TypeError, IndexError, KeyError):
                             pass
                     if modelAttribute is None:
                         value = node.get(attrTag)
                         if value is not None:
                             targetNodes.append(ModelAttribute(node,p.clarkNotation,UNKNOWN,value,value,value))
                     elif modelAttribute.xValid >= VALID:
                             targetNodes.append(modelAttribute)
             elif op == '/' or op is None:
                 if axis is None or axis == "child":
                     if isinstance(node,(ModelObject, etree._ElementTree)):
                         targetNodes = XmlUtil.children(node, ns, localname)
                 elif axis == "parent":
                     if isinstance(node,ModelAttribute):
                         parentNode = [ node.modelElement ]
                     else:
                         parentNode = [ XmlUtil.parent(node) ]
                     if (isinstance(node,ModelObject) and
                             (not ns or ns == parentNode.namespaceURI or ns == "*") and
                         (localname == parentNode.localName or localname == "*")):
                         targetNodes = [ parentNode ]
                 elif axis == "self":
                     if (isinstance(node,ModelObject) and
                             (not ns or ns == node.namespaceURI or ns == "*") and
                         (localname == node.localName or localname == "*")):
                         targetNodes = [ node ]
                 elif axis.startswith("descendant"):
                     if isinstance(node,(ModelObject, etree._ElementTree)):
                         targetNodes = XmlUtil.descendants(node, ns, localname)
                         if (axis.endswith("-or-self") and
                             isinstance(node,ModelObject) and
                             (not ns or ns == node.namespaceURI or ns == "*") and
                             (localname == node.localName or localname == "*")):
                             targetNodes.append(node) 
                 elif axis.startswith("ancestor"):
                     if isinstance(node,ModelObject):
                         targetNodes = [ancestor
                                        for ancestor in XmlUtil.ancestors(node)
                                        if ((not ns or ns == ancestor.namespaceURI or ns == "*") and
                                            (localname == ancestor.localName or localname == "*"))]
                         if (axis.endswith("-or-self") and
                             isinstance(node,ModelObject) and
                             (not ns or ns == node.namespaceURI or ns == "*") and
                             (localname == node.localName or localname == "*")):
                             targetNodes.insert(0, node) 
                 elif axis.endswith("-sibling"):
                     if isinstance(node,ModelObject):
                         targetNodes = [sibling
                                        for sibling in node.itersiblings(preceding=axis.startswith("preceding"))
                                        if ((not ns or ns == sibling.namespaceURI or ns == "*") and
                                            (localname == sibling.localName or localname == "*"))]
                 elif axis == "preceding":
                     if isinstance(node,ModelObject):
                         for preceding in node.getroottree().iter():
                             if preceding == node:
                                 break
                             elif ((not ns or ns == preceding.namespaceURI or ns == "*") and
                                   (localname == preceding.localName or localname == "*")):
                                 targetNodes.append(preceding)
                 elif axis == "following":
                     if isinstance(node,ModelObject):
                         foundNode = False
                         for following in node.getroottree().iter():
                             if following == node:
                                 foundNode = True
                             elif (foundNode and
                                   (not ns or ns == following.namespaceURI or ns == "*") and
                                   (localname == following.localName or localname == "*")):
                                 targetNodes.append(following)
             elif op == '//':
                 if isinstance(node,(ModelObject, etree. _ElementTree)):
                     targetNodes = XmlUtil.descendants(node, ns, localname)
             elif op == '..':
                 if isinstance(node,ModelAttribute):
                     targetNodes = [ node.modelElement ]
                 else:
                     targetNodes = [ XmlUtil.parent(node) ]
         elif isinstance(p, OperationDef) and isinstance(p.name,QNameDef):
             if isinstance(node,ModelObject):
                 if p.name.localName == "text": # note this is not string value, just child text
                     targetNodes = [node.textValue]
                 # todo: add element, attribute, node, etc...
         elif p == '*':  # wildcard
             if op == '/' or op is None:
                 if isinstance(node,(ModelObject, etree._ElementTree)):
                     targetNodes = XmlUtil.children(node, '*', '*')
             elif op == '//':
                 if isinstance(node,(ModelObject, etree._ElementTree)):
                     targetNodes = XmlUtil.descendants(node, '*', '*')
         targetSequence.extend(targetNodes)
     return targetSequence
示例#16
0
 def stepAxis(self, op, p, sourceSequence):
     targetSequence = []
     for node in sourceSequence:
         if not isinstance(
                 node, (ModelObject, etree._ElementTree, ModelAttribute)):
             raise XPathException(
                 self.progHeader, 'err:XPTY0020',
                 _('Axis step {0} context item is not a node: {1}').format(
                     op, node))
         targetNodes = []
         if isinstance(p, QNameDef):
             ns = p.namespaceURI
             localname = p.localName
             axis = p.axis
             if p.isAttribute:
                 if isinstance(node, ModelObject):
                     attrTag = p.localName if p.unprefixed else p.clarkNotation
                     modelAttribute = None
                     try:
                         modelAttribute = node.xAttributes[attrTag]
                     except (AttributeError, TypeError, IndexError,
                             KeyError):
                         # may be lax or deferred validated
                         try:
                             xmlValidate(node.modelXbrl, node, p)
                             modelAttribute = node.xAttributes[attrTag]
                         except (AttributeError, TypeError, IndexError,
                                 KeyError):
                             pass
                     if modelAttribute is None:
                         value = node.get(attrTag)
                         if value is not None:
                             targetNodes.append(
                                 ModelAttribute(node, p.clarkNotation,
                                                UNKNOWN, value, value,
                                                value))
                     elif modelAttribute.xValid >= VALID:
                         targetNodes.append(modelAttribute)
             elif op == '/' or op is None:
                 if axis is None or axis == "child":
                     if isinstance(node, (ModelObject, etree._ElementTree)):
                         targetNodes = XmlUtil.children(node, ns, localname)
                 elif axis == "parent":
                     if isinstance(node, ModelAttribute):
                         parentNode = [node.modelElement]
                     else:
                         parentNode = [XmlUtil.parent(node)]
                     if (isinstance(node, ModelObject)
                             and (not ns or ns == parentNode.namespaceURI
                                  or ns == "*")
                             and (localname == parentNode.localName
                                  or localname == "*")):
                         targetNodes = [parentNode]
                 elif axis == "self":
                     if (isinstance(node, ModelObject) and
                         (not ns or ns == node.namespaceURI or ns == "*")
                             and
                         (localname == node.localName or localname == "*")):
                         targetNodes = [node]
                 elif axis.startswith("descendant"):
                     if isinstance(node, (ModelObject, etree._ElementTree)):
                         targetNodes = XmlUtil.descendants(
                             node, ns, localname)
                         if (axis.endswith("-or-self")
                                 and isinstance(node, ModelObject)
                                 and (not ns or ns == node.namespaceURI
                                      or ns == "*")
                                 and (localname == node.localName
                                      or localname == "*")):
                             targetNodes.append(node)
                 elif axis.startswith("ancestor"):
                     if isinstance(node, ModelObject):
                         targetNodes = [
                             ancestor
                             for ancestor in XmlUtil.ancestors(node)
                             if ((not ns or ns == ancestor.namespaceURI
                                  or ns == "*") and (
                                      localname == ancestor.localName
                                      or localname == "*"))
                         ]
                         if (axis.endswith("-or-self")
                                 and isinstance(node, ModelObject)
                                 and (not ns or ns == node.namespaceURI
                                      or ns == "*")
                                 and (localname == node.localName
                                      or localname == "*")):
                             targetNodes.insert(0, node)
                 elif axis.endswith("-sibling"):
                     if isinstance(node, ModelObject):
                         targetNodes = [
                             sibling for sibling in node.itersiblings(
                                 preceding=axis.startswith("preceding"))
                             if ((not ns or ns == sibling.namespaceURI
                                  or ns == "*") and (
                                      localname == sibling.localName
                                      or localname == "*"))
                         ]
                 elif axis == "preceding":
                     if isinstance(node, ModelObject):
                         for preceding in node.getroottree().iter():
                             if preceding == node:
                                 break
                             elif ((not ns or ns == preceding.namespaceURI
                                    or ns == "*")
                                   and (localname == preceding.localName
                                        or localname == "*")):
                                 targetNodes.append(preceding)
                 elif axis == "following":
                     if isinstance(node, ModelObject):
                         foundNode = False
                         for following in node.getroottree().iter():
                             if following == node:
                                 foundNode = True
                             elif (foundNode and
                                   (not ns or ns == following.namespaceURI
                                    or ns == "*")
                                   and (localname == following.localName
                                        or localname == "*")):
                                 targetNodes.append(following)
             elif op == '//':
                 if isinstance(node, (ModelObject, etree._ElementTree)):
                     targetNodes = XmlUtil.descendants(node, ns, localname)
             elif op == '..':
                 if isinstance(node, ModelAttribute):
                     targetNodes = [node.modelElement]
                 else:
                     targetNodes = [XmlUtil.parent(node)]
         elif isinstance(p, OperationDef) and isinstance(p.name, QNameDef):
             if isinstance(node, ModelObject):
                 if p.name.localName == "text":  # note this is not string value, just child text
                     targetNodes = [node.textValue]
                 # todo: add element, attribute, node, etc...
         elif p == '*':  # wildcard
             if op == '/' or op is None:
                 if isinstance(node, (ModelObject, etree._ElementTree)):
                     targetNodes = XmlUtil.children(node, '*', '*')
             elif op == '//':
                 if isinstance(node, (ModelObject, etree._ElementTree)):
                     targetNodes = XmlUtil.descendants(node, '*', '*')
         targetSequence.extend(targetNodes)
     return targetSequence
示例#17
0
     def createModelFact(fact, parentModelFact, topTupleFact):
         aspects = fact.get("aspects", EMPTYDICT)
         if oimConcept not in aspects:
             modelXbrl.error("{}:conceptQName".format(errPrefix),
                             _("The concept QName could not be determined"),
                             modelObject=modelXbrl)
             return
         conceptQn = qname(aspects[oimConcept], prefixes)
         concept = modelXbrl.qnameConcepts.get(conceptQn)
         attrs = {}
         if concept.isItem:
             missingAspects = []
             if oimEntity not in aspects: 
                 missingAspects.append(oimEntity)
             if oimPeriod not in aspects and (oimPeriodStart not in aspects or oimPeriodEnd not in aspects):
                 missingAspects.append(oimPeriod)
             if missingAspects:
                 modelXbrl.error("{}:missingAspects".format(errPrefix),
                                 _("The concept %(element)s is missing aspects %(missingAspects)s"),
                                 modelObject=modelXbrl, element=conceptQn, missingAspects=", ".join(missingAspects))
                 return
             entityAsQn = qname(aspects[oimEntity], prefixes) or qname("error",fact[oimEntity])
             if oimPeriod in aspects:
                 periodStart = periodEnd = aspects[oimPeriod]
             if oimPeriodStart in aspects and oimPeriodEnd in aspects:
                 periodStart = aspects[oimPeriodStart]
                 periodEnd = aspects[oimPeriodEnd]
             cntxKey = ( # hashable context key
                 ("periodType", concept.periodType),
                 ("entity", entityAsQn),
                 ("periodStart", periodStart),
                 ("periodEnd", periodEnd)) + tuple(sorted(
                     (dimName, dimVal["value"] if isinstance(dimVal,dict) else dimVal) 
                     for dimName, dimVal in aspects.items()
                     if ":" in dimName and not dimName.startswith(oimPrefix)))
             if cntxKey in cntxTbl:
                 _cntx = cntxTbl[cntxKey]
             else:
                 cntxId = 'c-{:02}'.format(len(cntxTbl) + 1)
                 qnameDims = {}
                 for dimName, dimVal in aspects.items():
                     if ":" in dimName and not dimName.startswith(oimPrefix) and dimVal:
                         dimQname = qname(dimName, prefixes)
                         dimConcept = modelXbrl.qnameConcepts.get(dimQname)
                         if dimConcept is None:
                             modelXbrl.error("{}:taxonomyDefinedAspectQName".format(errPrefix),
                                             _("The taxonomy defined aspect concept QName %(qname)s could not be determined"),
                                             modelObject=modelXbrl, qname=dimQname)
                             continue
                         if isinstance(dimVal, dict):
                             dimVal = dimVal["value"]
                         else:
                             dimVal = str(dimVal) # may be int or boolean
                         if isinstance(dimVal,str) and ":" in dimVal and dimVal.partition(':')[0] in prefixes:
                             mem = qname(dimVal, prefixes) # explicit dim
                         elif dimConcept.isTypedDimension:
                             # a modelObject xml element is needed for all of the instance functions to manage the typed dim
                             mem = addChild(modelXbrl.modelDocument, dimConcept.typedDomainElement.qname, text=dimVal, appendChild=False)
                         qnameDims[dimQname] = DimValuePrototype(modelXbrl, None, dimQname, mem, "segment")
                 _cntx = modelXbrl.createContext(
                                         entityAsQn.namespaceURI,
                                         entityAsQn.localName,
                                         concept.periodType,
                                         None if concept.periodType == "instant" else dateTime(periodStart, type=DATETIME),
                                         dateTime(periodEnd, type=DATETIME),
                                         None, # no dimensional validity checking (like formula does)
                                         qnameDims, [], [],
                                         id=cntxId,
                                         beforeSibling=topTupleFact)
                 cntxTbl[cntxKey] = _cntx
             if oimUnit in aspects and concept.isNumeric:
                 unitKey = aspects[oimUnit]
                 if unitKey in unitTbl:
                     _unit = unitTbl[unitKey]
                 else:
                     _unit = None
                     # validate unit
                     unitKeySub = PrefixedQName.sub(UnitPrefixedQNameSubstitutionChar, unitKey)
                     if not UnitPattern.match(unitKeySub):
                         modelXbrl.error("oime:unitStringRepresentation",
                                         _("Unit string representation is lexically invalid, %(unit)s"),
                                         modelObject=modelXbrl, unit=unitKey)
                     else:
                         _mul, _sep, _div = unitKey.partition('/')
                         if _mul.startswith('('):
                             _mul = _mul[1:-1]
                         _muls = [u for u in _mul.split('*') if u]
                         if _div.startswith('('):
                             _div = _div[1:-1]
                         _divs = [u for u in _div.split('*') if u]
                         if _muls != sorted(_muls) or _divs != sorted(_divs):
                             modelXbrl.error("oime:unitStringRepresentation",
                                             _("Unit string representation measures are not in alphabetical order, %(unit)s"),
                                             modelObject=modelXbrl, unit=unitKey)
                         try:
                             mulQns = [qname(u, prefixes, prefixException=OIMException("oime:unitPrefix",
                                                                                       _("Unit prefix is not declared: %(unit)s"),
                                                                                       unit=u)) 
                                       for u in _muls]
                             divQns = [qname(u, prefixes, prefixException=OIMException("oime:unitPrefix",
                                                                                       _("Unit prefix is not declared: %(unit)s"),
                                                                                       unit=u))
                                       for u in _divs]
                             unitId = 'u-{:02}'.format(len(unitTbl) + 1)
                             for _measures in mulQns, divQns:
                                 for _measure in _measures:
                                     addQnameValue(modelXbrl.modelDocument, _measure)
                             _unit = modelXbrl.createUnit(mulQns, divQns, id=unitId, beforeSibling=topTupleFact)
                         except OIMException as ex:
                             modelXbrl.error(ex.code, ex.message, modelObject=modelXbrl, **ex.msgArgs)
                     unitTbl[unitKey] = _unit
             else:
                 _unit = None
         
             attrs["contextRef"] = _cntx.id
     
             if fact.get("value") is None:
                 attrs[XbrlConst.qnXsiNil] = "true"
                 text = None
             else:
                 text = fact["value"]
                 
             if concept.isNumeric:
                 if _unit is None:
                     return # skip creating fact because unit was invalid
                 attrs["unitRef"] = _unit.id
                 if "accuracy" in attrs or attrs.get(XbrlConst.qnXsiNil, "false") != "true":
                     attrs["decimals"] = fact.get("accuracy", "INF")
         else:
             text = None #tuple
                 
         id = fact.get("id")
         if id is not None:
             attrs["id"] = fact["id"]
                 
         # is value a QName?
         if concept.baseXbrliType == "QName":
             addQnameValue(modelXbrl.modelDocument, qname(text.strip(), prefixes))
 
         f = modelXbrl.createFact(conceptQn, attributes=attrs, text=text, parent=parentModelFact, validate=False)
         
         if id is not None and id in parentedFacts:
             # create child facts
             for i in sorted(parentedFacts[id],
                             key=lambda j: facts[j].get("aspects", EMPTYDICT).get(oimTupleOrder,0)):
                 createModelFact(facts[i], f, f if topTupleFact is None else topTupleFact)
                 
         # validate after creating tuple contents
         xmlValidate(modelXbrl, f)