def validate(modelXbrl, elt, recurse=True, attrQname=None, ixFacts=False): global ModelInlineValueObject, ixMsgCode if ModelInlineValueObject is None: from arelle.ModelInstanceObject import ModelInlineValueObject from arelle.XhtmlValidate import ixMsgCode isIxFact = isinstance(elt, ModelInlineValueObject) facets = None # attrQname can be provided for attributes that are global and LAX if (getattr(elt, "xValid", UNVALIDATED) == UNVALIDATED) and (not isIxFact or ixFacts): qnElt = elt.qname if ixFacts and isIxFact else elt.elementQname modelConcept = modelXbrl.qnameConcepts.get(qnElt) isAbstract = False if modelConcept is not None: isNillable = modelConcept.isNillable type = modelConcept.type if modelConcept.isAbstract: baseXsdType = "noContent" isAbstract = True elif modelConcept.isFraction: baseXsdType = "fraction" else: baseXsdType = modelConcept.baseXsdType facets = modelConcept.facets elif qnElt == XbrlConst.qnXbrldiExplicitMember: # not in DTS baseXsdType = "QName" type = None isNillable = False elif qnElt == XbrlConst.qnXbrldiTypedMember: # not in DTS baseXsdType = "noContent" type = None isNillable = False else: baseXsdType = None type = None isNillable = True # allow nil if no schema definition isNil = elt.get("{http://www.w3.org/2001/XMLSchema-instance}nil") in ( "true", "1") if attrQname is None: if isNil and not isNillable: if ModelInlineValueObject is not None and isinstance( elt, ModelInlineValueObject): errElt = "{0} fact {1}".format(elt.elementQname, elt.qname) else: errElt = elt.elementQname modelXbrl.error( "xmlSchema:nilNonNillableElement", _("Element %(element)s fact %(fact)s type %(typeName)s is nil but element has not been defined nillable" ), modelObject=elt, element=errElt, fact=elt.qname, typeName=modelConcept.baseXsdType if modelConcept is not None else "unknown") try: if isAbstract: raise ValueError("element is abstract") if isNil: text = "" elif baseXsdType == "noContent": text = elt.textValue # no descendant text nodes else: text = elt.stringValue # include descendant text nodes if modelConcept is not None: if len(text) == 0: if modelConcept.default is not None: text = modelConcept.default elif modelConcept.fixed is not None: text = modelConcept.fixed if baseXsdType == "token" and modelConcept.isEnumeration: if modelConcept.instanceOfType( XbrlConst.qnEnumeration2ItemTypes): baseXsdType = "enumerationHrefs" else: baseXsdType = "enumerationQNames" except Exception as err: if ModelInlineValueObject is not None and isinstance( elt, ModelInlineValueObject): errElt = "{0} fact {1}".format(elt.elementQname, elt.qname) else: errElt = elt.elementQname if isIxFact and err.__class__.__name__ == "FunctionArgType": modelXbrl.error( ixMsgCode("transformValueError", elt), _("Inline element %(element)s fact %(fact)s type %(typeName)s transform %(transform)s value error: %(value)s" ), modelObject=elt, element=errElt, fact=elt.qname, transform=elt.format, typeName=modelConcept.baseXsdType if modelConcept is not None else "unknown", value=XmlUtil.innerText(elt, ixExclude=True, ixContinuation=elt.namespaceURI == XbrlConst.ixbrl11)) elif isIxFact and err.__class__.__name__ == "ixtFunctionNotAvailable": modelXbrl.error( ixMsgCode("invalidTransformation", elt, sect="validation"), _("Fact %(fact)s has unrecognized transformation %(transform)s, value: %(value)s" ), modelObject=elt, element=errElt, fact=elt.qname, transform=elt.format, typeName=modelConcept.baseXsdType if modelConcept is not None else "unknown", value=XmlUtil.innerText(elt, ixExclude=True, ixContinuation=elt.namespaceURI == XbrlConst.ixbrl11)) elif isAbstract: modelXbrl.error( "xmlSchema:abstractElement", _("Element %(element)s has abstract declaration, value: %(value)s" ), modelObject=elt, element=errElt, error=str(err), value=elt.text) else: modelXbrl.error( "xmlSchema:valueError", _("Element %(element)s error %(error)s value: %(value)s" ), modelObject=elt, element=errElt, error=str(err), value=elt.text) elt.sValue = elt.xValue = text = INVALIDixVALUE elt.xValid = INVALID if text is not INVALIDixVALUE: validateValue(modelXbrl, elt, None, baseXsdType, text, isNillable, isNil, facets) # note that elt.sValue and elt.xValue are not innerText but only text elements on specific element (or attribute) if type is not None: definedAttributes = type.attributes else: definedAttributes = {} presentAttributes = set() # validate attributes # find missing attributes for default values for attrTag, attrValue in elt.items(): qn = qnameClarkName(attrTag) #qn = qname(attrTag, noPrefixIsNoNamespace=True) baseXsdAttrType = None facets = None if attrQname is not None: # validate all attributes and element if attrQname != qn: continue elif type is not None: presentAttributes.add(qn) if qn in definedAttributes: # look for concept-type-specific attribute definition modelAttr = definedAttributes[qn] elif qn.namespaceURI: # may be a globally defined attribute modelAttr = modelXbrl.qnameAttributes.get(qn) else: modelAttr = None if modelAttr is not None: baseXsdAttrType = modelAttr.baseXsdType facets = modelAttr.facets if baseXsdAttrType is None: # look for global attribute definition attrObject = modelXbrl.qnameAttributes.get(qn) if attrObject is not None: baseXsdAttrType = attrObject.baseXsdType facets = attrObject.facets elif attrTag == "{http://xbrl.org/2006/xbrldi}dimension": # some fallbacks? baseXsdAttrType = "QName" elif attrTag == "id": baseXsdAttrType = "ID" elif elt.namespaceURI == "http://www.w3.org/2001/XMLSchema": if attrTag in {"type", "ref", "base", "refer", "itemType"}: baseXsdAttrType = "QName" elif attrTag in {"name"}: baseXsdAttrType = "NCName" elif attrTag in {"default", "fixed", "form"}: baseXsdAttrType = "string" elif elt.namespaceURI == "http://xbrl.org/2006/xbrldi": if attrTag == "dimension": baseXsdAttrType = "QName" elif qn in predefinedAttributeTypes: baseXsdAttrType, facets = predefinedAttributeTypes[qn] validateValue(modelXbrl, elt, attrTag, baseXsdAttrType, attrValue, facets=facets) # if no attributes assigned above, there won't be an xAttributes, if so assign a shared dict to save memory try: elt.xAttributes except AttributeError: elt.xAttributes = xAttributesSharedEmptyDict if type is not None: if attrQname is None: missingAttributes = type.requiredAttributeQnames - presentAttributes - elt.slottedAttributesNames if missingAttributes: modelXbrl.error( "xmlSchema:attributesRequired", _("Element %(element)s type %(typeName)s missing required attributes: %(attributes)s" ), modelObject=elt, element=qnElt, typeName=baseXsdType, attributes=','.join(str(a) for a in missingAttributes)) extraAttributes = presentAttributes - _DICT_SET( definedAttributes.keys()) - XbrlConst.builtinAttributes if extraAttributes: attributeWildcards = type.attributeWildcards extraAttributes -= set( a for a in extraAttributes if validateAnyWildcard(qnElt, a, attributeWildcards)) if isIxFact: extraAttributes -= XbrlConst.ixAttributes if extraAttributes: modelXbrl.error( "xmlSchema:attributesExtraneous", _("Element %(element)s type %(typeName)s extraneous attributes: %(attributes)s" ), modelObject=elt, element=qnElt, typeName=baseXsdType, attributes=','.join( str(a) for a in extraAttributes)) # add default attribute values for attrQname in (type.defaultAttributeQnames - presentAttributes): modelAttr = type.attributes[attrQname] validateValue(modelXbrl, elt, attrQname.clarkNotation, modelAttr.baseXsdType, modelAttr.default, facets=modelAttr.facets) if recurse: global validateElementSequence, modelGroupCompositorTitle if validateElementSequence is None: from arelle.XmlValidateParticles import validateElementSequence, modelGroupCompositorTitle try: #childElts = list(elt) # uses __iter__ for inline facts childElts = [e for e in elt if isinstance(e, ModelObject)] if isNil: if childElts or elt.text: modelXbrl.error( "xmlSchema:nilElementHasContent", _("Element %(element)s is nil but has contents" ), modelObject=elt, element=qnElt) else: errResult = validateElementSequence( modelXbrl, type, childElts, ixFacts) if errResult is not None and errResult[2]: iElt, occured, errDesc, errArgs = errResult errElt = childElts[iElt] if iElt < len( childElts) else elt errArgs["modelObject"] = errElt errArgs["element"] = errElt.qname errArgs["parentElement"] = elt.qname if "compositor" in errArgs: # compositor is an object, provide friendly string errArgs[ "compositor"] = modelGroupCompositorTitle( errArgs["compositor"]) modelXbrl.error(*errDesc, **errArgs) # when error is in an xbrli element, check any further unvalidated children if qnElt.namespaceURI == XbrlConst.xbrli and iElt < len( childElts): for childElt in childElts[iElt:]: if (getattr(childElt, "xValid", UNVALIDATED) == UNVALIDATED): validate(modelXbrl, childElt, ixFacts=ixFacts) recurse = False # cancel child element validation below, recursion was within validateElementSequence except AttributeError as ex: raise ex #pass # HF Why is this here???? if recurse: # if there is no complex or simple type (such as xbrli:measure) then this code is used for child in (elt.modelTupleFacts if ixFacts and isIxFact else elt): if isinstance(child, ModelObject): validate(modelXbrl, child, recurse, attrQname, ixFacts)
def validate(modelXbrl, elt, recurse=True, attrQname=None, ixFacts=False): global ModelInlineValueObject, ixMsgCode if ModelInlineValueObject is None: from arelle.ModelInstanceObject import ModelInlineValueObject from arelle.XhtmlValidate import ixMsgCode isIxFact = isinstance(elt, ModelInlineValueObject) facets = None # attrQname can be provided for attributes that are global and LAX if (getattr(elt,"xValid", UNVALIDATED) == UNVALIDATED) and (not isIxFact or ixFacts): qnElt = elt.qname if ixFacts and isIxFact else elt.elementQname modelConcept = modelXbrl.qnameConcepts.get(qnElt) isAbstract = False if modelConcept is not None: isNillable = modelConcept.isNillable type = modelConcept.type if modelConcept.isAbstract: baseXsdType = "noContent" isAbstract = True elif modelConcept.isFraction: baseXsdType = "fraction" else: baseXsdType = modelConcept.baseXsdType facets = modelConcept.facets elif qnElt == XbrlConst.qnXbrldiExplicitMember: # not in DTS baseXsdType = "QName" type = None isNillable = False elif qnElt == XbrlConst.qnXbrldiTypedMember: # not in DTS baseXsdType = "noContent" type = None isNillable = False else: baseXsdType = None type = None isNillable = True # allow nil if no schema definition isNil = elt.get("{http://www.w3.org/2001/XMLSchema-instance}nil") in ("true", "1") if attrQname is None: if isNil and not isNillable: if ModelInlineValueObject is not None and isinstance(elt, ModelInlineValueObject): errElt = "{0} fact {1}".format(elt.elementQname, elt.qname) else: errElt = elt.elementQname modelXbrl.error("xmlSchema:nilNonNillableElement", _("Element %(element)s fact %(fact)s type %(typeName)s is nil but element has not been defined nillable"), modelObject=elt, element=errElt, fact=elt.qname, transform=elt.format, typeName=modelConcept.baseXsdType if modelConcept is not None else "unknown", value=XmlUtil.innerText(elt, ixExclude=True)) try: if isAbstract: raise ValueError("element is abstract") if isNil: text = "" elif baseXsdType == "noContent": text = elt.textValue # no descendant text nodes else: text = elt.stringValue # include descendant text nodes if len(text) == 0 and modelConcept is not None: if modelConcept.default is not None: text = modelConcept.default elif modelConcept.fixed is not None: text = modelConcept.fixed except Exception as err: if ModelInlineValueObject is not None and isinstance(elt, ModelInlineValueObject): errElt = "{0} fact {1}".format(elt.elementQname, elt.qname) else: errElt = elt.elementQname if isIxFact and err.__class__.__name__ == "FunctionArgType": modelXbrl.error(ixMsgCode("transformValueError", elt), _("Inline element %(element)s fact %(fact)s type %(typeName)s transform %(transform)s value error: %(value)s"), modelObject=elt, element=errElt, fact=elt.qname, transform=elt.format, typeName=modelConcept.baseXsdType if modelConcept is not None else "unknown", value=XmlUtil.innerText(elt, ixExclude=True, ixContinuation=elt.namespaceURI==XbrlConst.ixbrl11)) elif isIxFact and err.__class__.__name__ == "ixtFunctionNotAvailable": modelXbrl.error(ixMsgCode("formatCodeUndefined", elt), _("Inline element %(element)s fact %(fact)s type %(typeName)s transform %(transform)s not available, value: %(value)s"), modelObject=elt, element=errElt, fact=elt.qname, transform=elt.format, typeName=modelConcept.baseXsdType if modelConcept is not None else "unknown", value=XmlUtil.innerText(elt, ixExclude=True, ixContinuation=elt.namespaceURI==XbrlConst.ixbrl11)) elif isAbstract: modelXbrl.error("xmlSchema:abstractElement", _("Element %(element)s has abstract declaration, value: %(value)s"), modelObject=elt, element=errElt, error=str(err), value=elt.text) else: modelXbrl.error("xmlSchema:valueError", _("Element %(element)s error %(error)s value: %(value)s"), modelObject=elt, element=errElt, error=str(err), value=elt.text) elt.sValue = elt.xValue = text = INVALIDixVALUE elt.xValid = INVALID if text is not INVALIDixVALUE: validateValue(modelXbrl, elt, None, baseXsdType, text, isNillable, isNil, facets) # note that elt.sValue and elt.xValue are not innerText but only text elements on specific element (or attribute) if type is not None: definedAttributes = type.attributes else: definedAttributes = {} presentAttributes = set() # validate attributes # find missing attributes for default values for attrTag, attrValue in elt.items(): qn = qnameClarkName(attrTag) #qn = qname(attrTag, noPrefixIsNoNamespace=True) baseXsdAttrType = None facets = None if attrQname is not None: # validate all attributes and element if attrQname != qn: continue elif type is not None: presentAttributes.add(qn) if qn in definedAttributes: # look for concept-type-specific attribute definition modelAttr = definedAttributes[qn] elif qn.namespaceURI: # may be a globally defined attribute modelAttr = modelXbrl.qnameAttributes.get(qn) else: modelAttr = None if modelAttr is not None: baseXsdAttrType = modelAttr.baseXsdType facets = modelAttr.facets if baseXsdAttrType is None: # look for global attribute definition attrObject = modelXbrl.qnameAttributes.get(qn) if attrObject is not None: baseXsdAttrType = attrObject.baseXsdType facets = attrObject.facets elif attrTag == "{http://xbrl.org/2006/xbrldi}dimension": # some fallbacks? baseXsdAttrType = "QName" elif attrTag == "id": baseXsdAttrType = "ID" elif elt.namespaceURI == "http://www.w3.org/2001/XMLSchema": if attrTag in {"type", "ref", "base", "refer", "itemType"}: baseXsdAttrType = "QName" elif attrTag in {"name"}: baseXsdAttrType = "NCName" elif attrTag in {"default", "fixed", "form"}: baseXsdAttrType = "string" elif elt.namespaceURI == "http://xbrl.org/2006/xbrldi": if attrTag == "dimension": baseXsdAttrType = "QName" elif qn in predefinedAttributeTypes: baseXsdAttrType, facets = predefinedAttributeTypes[qn] validateValue(modelXbrl, elt, attrTag, baseXsdAttrType, attrValue, facets=facets) # if no attributes assigned above, there won't be an xAttributes, if so assign a shared dict to save memory try: elt.xAttributes except AttributeError: elt.xAttributes = xAttributesSharedEmptyDict if type is not None: if attrQname is None: missingAttributes = type.requiredAttributeQnames - presentAttributes - elt.slottedAttributesNames if missingAttributes: modelXbrl.error("xmlSchema:attributesRequired", _("Element %(element)s type %(typeName)s missing required attributes: %(attributes)s"), modelObject=elt, element=qnElt, typeName=baseXsdType, attributes=','.join(str(a) for a in missingAttributes)) extraAttributes = presentAttributes - _DICT_SET(definedAttributes.keys()) - XbrlConst.builtinAttributes if extraAttributes: attributeWildcards = type.attributeWildcards extraAttributes -= set(a for a in extraAttributes if validateAnyWildcard(qnElt, a, attributeWildcards)) if isIxFact: extraAttributes -= XbrlConst.ixAttributes if extraAttributes: modelXbrl.error("xmlSchema:attributesExtraneous", _("Element %(element)s type %(typeName)s extraneous attributes: %(attributes)s"), modelObject=elt, element=qnElt, typeName=baseXsdType, attributes=','.join(str(a) for a in extraAttributes)) # add default attribute values for attrQname in (type.defaultAttributeQnames - presentAttributes): modelAttr = type.attributes[attrQname] validateValue(modelXbrl, elt, attrQname.clarkNotation, modelAttr.baseXsdType, modelAttr.default, facets=modelAttr.facets) if recurse: global validateElementSequence, modelGroupCompositorTitle if validateElementSequence is None: from arelle.XmlValidateParticles import validateElementSequence, modelGroupCompositorTitle try: #childElts = list(elt) # uses __iter__ for inline facts childElts = [e for e in elt if isinstance(e, ModelObject)] if isNil: if childElts or elt.text: modelXbrl.error("xmlSchema:nilElementHasContent", _("Element %(element)s is nil but has contents"), modelObject=elt, element=qnElt) else: errResult = validateElementSequence(modelXbrl, type, childElts, ixFacts) if errResult is not None and errResult[2]: iElt, occured, errDesc, errArgs = errResult errElt = childElts[iElt] if iElt < len(childElts) else elt errArgs["modelObject"] = errElt errArgs["element"] = errElt.qname errArgs["parentElement"] = elt.qname if "compositor" in errArgs: # compositor is an object, provide friendly string errArgs["compositor"] = modelGroupCompositorTitle(errArgs["compositor"]) modelXbrl.error(*errDesc,**errArgs) # when error is in an xbrli element, check any further unvalidated children if qnElt.namespaceURI == XbrlConst.xbrli and iElt < len(childElts): for childElt in childElts[iElt:]: if (getattr(childElt,"xValid", UNVALIDATED) == UNVALIDATED): validate(modelXbrl, childElt, ixFacts=ixFacts) recurse = False # cancel child element validation below, recursion was within validateElementSequence except AttributeError as ex: raise ex #pass # HF Why is this here???? if recurse: # if there is no complex or simple type (such as xbrli:measure) then this code is used for child in (elt.modelTupleFacts if ixFacts and isIxFact else elt): if isinstance(child, ModelObject): validate(modelXbrl, child, recurse, attrQname, ixFacts)