def stepAxis(self, op, p, sourceSequence): targetSequence = [] for node in sourceSequence: if not isinstance(node,(ModelObject.ModelObject, xml.dom.Node)): raise XPathException(p, 'err:XPTY0020', _('Axis step {0} context item is not a node: {1}').format(op, node)) targetNodes = [] if isinstance(node, ModelObject.ModelObject): node = node.element if isinstance(p,QNameDef): ns = p.namespaceURI; localname = p.localName if p.isAttribute: if p.unprefixed: if node.hasAttribute(localname): targetNodes.append(node.getAttribute(localname)) else: if node.hasAttributeNS(ns,localname): targetNodes.append(node.getAttributeNS(ns,localname)) elif op == '/' or op is None: targetNodes = XmlUtil.children(node, ns, localname) elif op == '//': targetNodes = XmlUtil.descendants(node, ns, localname) elif op == '..': targetNodes = [ XmlUtil.parent(node) ] elif isinstance(p, OperationDef) and isinstance(p.name,QNameDef): if p.name.localName == "text": targetNodes = [XmlUtil.text(node)] # todo: add element, attribute, node, etc... targetSequence.extend(targetNodes) return targetSequence
def stepAxis(self, op, p, sourceSequence): targetSequence = [] for node in sourceSequence: if not isinstance(node, (ModelObject.ModelObject, xml.dom.Node)): raise XPathException( p, 'err:XPTY0020', _('Axis step {0} context item is not a node: {1}').format( op, node)) targetNodes = [] if isinstance(node, ModelObject.ModelObject): node = node.element if isinstance(p, QNameDef): ns = p.namespaceURI localname = p.localName if p.isAttribute: if p.unprefixed: if node.hasAttribute(localname): targetNodes.append(node.getAttribute(localname)) else: if node.hasAttributeNS(ns, localname): targetNodes.append( node.getAttributeNS(ns, localname)) elif op == '/' or op is None: targetNodes = XmlUtil.children(node, ns, localname) elif op == '//': targetNodes = XmlUtil.descendants(node, ns, localname) elif op == '..': targetNodes = [XmlUtil.parent(node)] elif isinstance(p, OperationDef) and isinstance(p.name, QNameDef): if p.name.localName == "text": targetNodes = [XmlUtil.text(node)] # todo: add element, attribute, node, etc... targetSequence.extend(targetNodes) return targetSequence
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 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: validate(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 isinstance(node,(ModelObject, etree._ElementTree)): targetNodes = XmlUtil.children(node, ns, localname) 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": targetNodes = [XmlUtil.text(node)] # 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
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
def evaluate(self, exprStack, contextItem=None, resultStack=None, parentOp=None): if resultStack is None: resultStack = [] if contextItem is None: contextItem = self.contextItem setProgHeader = False for p in exprStack: result = None if isinstance(p, QNameDef) or (p == '*' and parentOp in ('/', '//') ): # path step QName or wildcard # step axis operation if len(resultStack) == 0 or not self.isNodeSequence( resultStack[-1]): resultStack.append([ contextItem, ]) result = self.stepAxis(parentOp, p, resultStack.pop()) elif isinstance(p, _STR_NUM_TYPES): result = p elif isinstance(p, VariableRef): if p.name in self.inScopeVars: result = self.inScopeVars[p.name] # uncomment to allow lambdas as variable values (for deferred processing if needed) #if isinstance(result, LambdaType): # result = result() # dereference lambda-valued variables if result is None: # None atomic result is XPath empty sequence result = [ ] # subsequent processing discards None results elif isinstance(p, OperationDef): op = p.name if isinstance(op, QNameDef): # function call args = self.evaluate(p.args, contextItem=contextItem) ns = op.namespaceURI localname = op.localName try: from arelle import (FunctionXs, FunctionFn, FunctionXfi, FunctionIxt, FunctionCustom) if op in self.modelXbrl.modelCustomFunctionSignatures: result = FunctionCustom.call( self, p, op, contextItem, args) elif op in self.customFunctions: # plug in method custom functions result = self.customFunctions[op]( self, p, contextItem, args) # use plug-in's method elif op.unprefixed and localname in { 'attribute', 'comment', 'document-node', 'element', 'item', 'node', 'processing-instruction', 'schema-attribute', 'schema-element', 'text' }: # step axis operation if len(resultStack ) == 0 or not self.isNodeSequence( resultStack[-1]): if isinstance(contextItem, (tuple, list)): resultStack.append(contextItem) else: resultStack.append([ contextItem, ]) result = self.stepAxis(parentOp, p, resultStack.pop()) elif op.unprefixed or ns == XbrlConst.fn: result = FunctionFn.call(self, p, localname, contextItem, args) elif ns == XbrlConst.xfi or ns == XbrlConst.xff: result = FunctionXfi.call(self, p, localname, args) elif ns == XbrlConst.xsd: result = FunctionXs.call(self, p, localname, args) elif ns in FunctionIxt.ixtNamespaceFunctions: result = FunctionIxt.call(self, p, op, args) elif op in self.modelXbrl.modelManager.customTransforms: result = self.modelXbrl.modelManager.customTransforms[ op](args[0][0]) else: raise XPathException( p, 'err:XPST0017', _('Function call not identified: {0}.').format( op)) except FunctionNumArgs as err: raise XPathException(p, err.errCode, "{}: {}".format(err.errText, op)) except FunctionArgType as err: raise XPathException( p, err.errCode, _('Argument {0} does not match expected type {1} for {2} {3}.' ).format(err.argNum, err.expectedType, op, err.foundObject)) except FunctionNotAvailable: raise XPathException( p, 'err:XPST0017', _('Function named {0} does not have a custom or built-in implementation.' ).format(op)) elif op in VALUE_OPS: # binary arithmetic operations and value comparisons s1 = self.atomize( p, resultStack.pop()) if len(resultStack) > 0 else [] s2 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem)) # value comparisons if len(s1) > 1 or len(s2) > 1: raise XPathException( p, 'err:XPTY0004', _("Value operation '{0}' sequence length error"). format(op)) if len(s1) == 0 or len(s2) == 0: result = [] else: op1 = s1[0] op2 = s2[0] testTypeCompatiblity(self, p, op, op1, op2) if type(op1) != type(op2) and op in ('+', '-', '*', 'div', 'idiv', 'mod'): # check if type promotion needed (Decimal-float, not needed for integer-Decimal) if isinstance(op1, Decimal) and isinstance( op2, float): op1 = float( op1 ) # per http://http://www.w3.org/TR/xpath20/#dt-type-promotion 1b elif isinstance(op2, Decimal) and isinstance( op1, float): op2 = float(op2) if op == '+': result = op1 + op2 elif op == '-': result = op1 - op2 elif op == '*': result = op1 * op2 elif op in ('div', 'idiv', "mod"): try: if op == 'div': result = op1 / op2 elif op == 'idiv': result = op1 // op2 elif op == 'mod': result = op1 % op2 except ZeroDivisionError: raise XPathException( p, 'err:FOAR0001', _('Attempt to divide by zero: {0} {1} {2}.' ).format(op1, op, op2)) elif op == 'ge': result = op1 >= op2 elif op == 'gt': result = op1 > op2 elif op == 'le': result = op1 <= op2 elif op == 'lt': result = op1 < op2 elif op == 'eq': result = op1 == op2 elif op == 'ne': result = op1 != op2 elif op == 'to': result = _RANGE(_INT(op1), _INT(op2) + 1) elif op in GENERALCOMPARISON_OPS: # general comparisons s1 = self.atomize( p, resultStack.pop()) if len(resultStack) > 0 else [] s2 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem)) result = [] for op1 in s1: for op2 in s2: testTypeCompatiblity(self, p, op, op1, op2) if op == '>=': result = op1 >= op2 elif op == '>': result = op1 > op2 elif op == '<=': result = op1 <= op2 elif op == '<': result = op1 < op2 elif op == '=': result = op1 == op2 elif op == '!=': result = op1 != op2 if result: break if result: break elif op in NODECOMPARISON_OPS: # node comparisons s1 = resultStack.pop() if len(resultStack) > 0 else [] s2 = self.evaluate(p.args, contextItem=contextItem) if len(s1) > 1 or len(s2) > 1 or not self.isNodeSequence( s1) or not self.isNodeSequence(s2[0]): raise XPathException( p, 'err:XPTY0004', _('Node comparison sequence error')) if len(s1) == 0 or len(s2[0]) == 0: result = [] else: n1 = s1[0] n2 = s2[0][0] result = False for op1 in s1: for op2 in s2: if op == 'is': result = n1 == n2 elif op == '>>': result = op1 > op2 elif op == '<<': result = op1 <= op2 if result: break elif op in COMBINING_OPS: # node comparisons s1 = resultStack.pop() if len(resultStack) > 0 else [] s2 = self.flattenSequence( self.evaluate(p.args, contextItem=contextItem)) if not self.isNodeSequence(s1) or not self.isNodeSequence( s2): raise XPathException( p, 'err:XPTY0004', _('Node operation sequence error')) set1 = set(s1) set2 = set(s2) if op == 'intersect': resultset = set1 & set2 elif op == 'except': resultset = set1 - set2 elif op == 'union' or op == '|': resultset = set1 | set2 # convert to a list in document order result = self.documentOrderedNodes(resultset) elif op in LOGICAL_OPS: # general comparisons if len(resultStack) == 0: result = [] else: op1 = self.effectiveBooleanValue(p, resultStack.pop( )) if len(resultStack) > 0 else False # consider short circuit possibilities if op == 'or' and op1: result = True elif op == 'and' and not op1: result = False else: # must evaluate other operand op2 = self.effectiveBooleanValue( p, self.evaluate(p.args, contextItem=contextItem)) if op == 'and': result = op1 and op2 elif op == 'or': result = op1 or op2 elif op in UNARY_OPS: s1 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem)) if len(s1) > 1: raise XPathException( p, 'err:XPTY0004', _('Unary expression sequence length error')) if len(s1) == 0: result = [] else: op1 = s1[0] if op == 'u+': result = op1 elif op == 'u-': result = -op1 elif op == 'instance': result = False s1 = self.flattenSequence( resultStack.pop()) if len(resultStack) > 0 else [] arity = len(s1) if len(p.args) > 1: occurenceIndicator = p.args[1] if (occurenceIndicator == '?' and arity in (0,1) ) or \ (occurenceIndicator == '+' and arity >= 1) or \ (occurenceIndicator == '*'): result = True elif arity == 1: result = True if result and len(p.args) > 0: t = p.args[0] for x in s1: if isinstance(t, QNameDef): if t.namespaceURI == XbrlConst.xsd: tType = { "integer": _INT_TYPES, "string": _STR_BASE, "decimal": Decimal, "double": float, "float": float, "boolean": bool, "QName": QName, "anyURI": AnyURI, "date": DateTime, "dateTime": DateTime, }.get(t.localName) if tType: result = isinstance(x, tType) if result and tType == DateTime: result = x.dateOnly == ( t.localName == "date") elif isinstance(t, OperationDef): if t.name == "element": if isinstance(x, ModelObject): if len(t.args) >= 1: qn = t.args[0] if qn == '*' or (isinstance( qn, QNameDef) and qn == x): result = True if len(t.args ) >= 2 and isinstance( t.args[1], QNameDef): modelXbrl = x.modelDocument.modelXbrl modelConcept = modelXbrl.qnameConcepts.get( qname(x)) if not modelConcept.instanceOfType( t.args[1]): result = False else: result = False # elif t.name == "item" comes here and result stays True if not result: break elif op == 'sequence': result = self.evaluate(p.args, contextItem=contextItem) elif op == 'predicate': result = self.predicate( p, resultStack.pop()) if len(resultStack) > 0 else [] elif op in FORSOMEEVERY_OPS: # for, some, every result = [] self.evaluateRangeVars(op, p.args[0], p.args[1:], contextItem, result) elif op == 'if': test = self.effectiveBooleanValue( p, self.evaluate(p.args[0].expr[0], contextItem=contextItem)) result = self.evaluate(p.args[1 if test else 2].args, contextItem=contextItem) elif op == '.': result = contextItem elif op == '..': result = XmlUtil.parent(contextItem) elif op in PATH_OPS: if op in ('rootChild', 'rootDescendant'): # fix up for multi-instance resultStack.append([ self.inputXbrlInstance.xmlDocument, ]) op = '/' if op == 'rootChild' else '//' # contains QNameDefs and predicates if len(resultStack) > 0: innerFocusNodes = resultStack.pop() else: innerFocusNodes = contextItem navSequence = [] for innerFocusNode in self.flattenSequence( innerFocusNodes): navSequence += self.evaluate( p.args, contextItem=innerFocusNode, parentOp=op) result = self.documentOrderedNodes( self.flattenSequence(navSequence)) elif isinstance(p, ProgHeader): self.progHeader = p if p.traceType not in (Trace.MESSAGE, Trace.CUSTOM_FUNCTION): self.traceType = p.traceType setProgHeader = True if result is not None: # note: result can be False which gets appended to resultStack resultStack.append(self.flattenSequence(result)) if setProgHeader: self.progHeader = None return resultStack
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
def evaluate(self, exprStack, contextItem=None, resultStack=None, parentOp=None): if resultStack is None: resultStack = [] if contextItem is None: contextItem = self.contextItem setProgHeader = False for p in exprStack: result = None if isinstance(p,QNameDef) or (p == '*' and parentOp in ('/', '//')): # path step QName or wildcard # step axis operation if len(resultStack) == 0 or not self.isNodeSequence(resultStack[-1]): resultStack.append( [ contextItem, ] ) result = self.stepAxis(parentOp, p, resultStack.pop() ) elif isinstance(p,_STR_NUM_TYPES): result = p elif isinstance(p,VariableRef): if p.name in self.inScopeVars: result = self.inScopeVars[p.name] # uncomment to allow lambdas as variable values (for deferred processing if needed) #if isinstance(result, LambdaType): # result = result() # dereference lambda-valued variables if result is None: # None atomic result is XPath empty sequence result = [] # subsequent processing discards None results elif isinstance(p,OperationDef): op = p.name if isinstance(op, QNameDef): # function call args = self.evaluate(p.args, contextItem=contextItem) ns = op.namespaceURI; localname = op.localName try: from arelle import (FunctionXs, FunctionFn, FunctionXfi, FunctionIxt, FunctionCustom) if op in self.modelXbrl.modelCustomFunctionSignatures: result = FunctionCustom.call(self, p, op, contextItem, args) elif op in self.customFunctions: # plug in method custom functions result = self.customFunctions[op](self, p, contextItem, args) # use plug-in's method elif op.unprefixed and localname in {'attribute', 'comment', 'document-node', 'element', 'item', 'node', 'processing-instruction', 'schema-attribute', 'schema-element', 'text'}: # step axis operation if len(resultStack) == 0 or not self.isNodeSequence(resultStack[-1]): if isinstance(contextItem, (tuple,list)): resultStack.append( contextItem ) else: resultStack.append( [ contextItem, ] ) result = self.stepAxis(parentOp, p, resultStack.pop() ) elif op.unprefixed or ns == XbrlConst.fn: result = FunctionFn.call(self, p, localname, contextItem, args) elif ns == XbrlConst.xfi or ns == XbrlConst.xff: result = FunctionXfi.call(self, p, localname, args) elif ns == XbrlConst.xsd: result = FunctionXs.call(self, p, localname, args) elif ns in FunctionIxt.ixtNamespaceFunctions: result = FunctionIxt.call(self, p, op, args) elif op in self.modelXbrl.modelManager.customTransforms: result = self.modelXbrl.modelManager.customTransforms[op](args[0][0]) else: raise XPathException(p, 'err:XPST0017', _('Function call not identified: {0}.').format(op)) except FunctionNumArgs as err: raise XPathException(p, err.errCode, "{}: {}".format(err.errText, op)) except FunctionArgType as err: raise XPathException(p, err.errCode, _('Argument {0} does not match expected type {1} for {2} {3}.') .format(err.argNum, err.expectedType, op, err.foundObject)) except FunctionNotAvailable: raise XPathException(p, 'err:XPST0017', _('Function named {0} does not have a custom or built-in implementation.').format(op)) elif op in VALUE_OPS: # binary arithmetic operations and value comparisons s1 = self.atomize( p, resultStack.pop() ) if len(resultStack) > 0 else [] s2 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem) ) # value comparisons if len(s1) > 1 or len(s2) > 1: raise XPathException(p, 'err:XPTY0004', _("Value operation '{0}' sequence length error").format(op)) if len(s1) == 0 or len(s2) == 0: result = [] else: op1 = s1[0] op2 = s2[0] testTypeCompatiblity( self, p, op, op1, op2 ) if type(op1) != type(op2) and op in ('+', '-', '*', 'div', 'idiv', 'mod'): # check if type promotion needed (Decimal-float, not needed for integer-Decimal) if isinstance(op1,Decimal) and isinstance(op2,float): op1 = float(op1) # per http://http://www.w3.org/TR/xpath20/#dt-type-promotion 1b elif isinstance(op2,Decimal) and isinstance(op1,float): op2 = float(op2) if op == '+': result = op1 + op2 elif op == '-': result = op1 - op2 elif op == '*': result = op1 * op2 elif op in ('div', 'idiv', "mod"): try: if op == 'div': result = op1 / op2 elif op == 'idiv': result = op1 // op2 elif op == 'mod': result = op1 % op2 except ZeroDivisionError: raise XPathException(p, 'err:FOAR0001', _('Attempt to divide by zero: {0} {1} {2}.') .format(op1, op, op2)) elif op == 'ge': result = op1 >= op2 elif op == 'gt': result = op1 > op2 elif op == 'le': result = op1 <= op2 elif op == 'lt': result = op1 < op2 elif op == 'eq': result = op1 == op2 elif op == 'ne': result = op1 != op2 elif op == 'to': result = _RANGE( _INT(op1), _INT(op2) + 1 ) elif op in GENERALCOMPARISON_OPS: # general comparisons s1 = self.atomize( p, resultStack.pop() ) if len(resultStack) > 0 else [] s2 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem) ) result = []; for op1 in s1: for op2 in s2: testTypeCompatiblity( self, p, op, op1, op2 ) if op == '>=': result = op1 >= op2 elif op == '>': result = op1 > op2 elif op == '<=': result = op1 <= op2 elif op == '<': result = op1 < op2 elif op == '=': result = op1 == op2 elif op == '!=': result = op1 != op2 if result: break if result: break elif op in NODECOMPARISON_OPS: # node comparisons s1 = resultStack.pop() if len(resultStack) > 0 else [] s2 = self.evaluate(p.args, contextItem=contextItem) if len(s1) > 1 or len(s2) > 1 or not self.isNodeSequence(s1) or not self.isNodeSequence(s2[0]): raise XPathException(p, 'err:XPTY0004', _('Node comparison sequence error')) if len(s1) == 0 or len(s2[0]) == 0: result = [] else: n1 = s1[0] n2 = s2[0][0] result = False; for op1 in s1: for op2 in s2: if op == 'is': result = n1 == n2 elif op == '>>': result = op1 > op2 elif op == '<<': result = op1 <= op2 if result: break elif op in COMBINING_OPS: # node comparisons s1 = resultStack.pop() if len(resultStack) > 0 else [] s2 = self.flattenSequence(self.evaluate(p.args, contextItem=contextItem)) if not self.isNodeSequence(s1) or not self.isNodeSequence(s2): raise XPathException(p, 'err:XPTY0004', _('Node operation sequence error')) set1 = set(s1) set2 = set(s2) if op == 'intersect': resultset = set1 & set2 elif op == 'except': resultset = set1 - set2 elif op == 'union' or op == '|': resultset = set1 | set2 # convert to a list in document order result = self.documentOrderedNodes(resultset) elif op in LOGICAL_OPS: # general comparisons if len(resultStack) == 0: result = [] else: op1 = self.effectiveBooleanValue( p, resultStack.pop() ) if len(resultStack) > 0 else False # consider short circuit possibilities if op == 'or' and op1: result = True elif op == 'and' and not op1: result = False else: # must evaluate other operand op2 = self.effectiveBooleanValue( p, self.evaluate(p.args, contextItem=contextItem) ) if op == 'and': result = op1 and op2 elif op == 'or': result = op1 or op2 elif op in UNARY_OPS: s1 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem) ) if len(s1) > 1: raise XPathException(p, 'err:XPTY0004', _('Unary expression sequence length error')) if len(s1) == 0: result = [] else: op1 = s1[0] if op == 'u+': result = op1 elif op == 'u-': result = -op1 elif op == 'instance': result = False s1 = self.flattenSequence( resultStack.pop() ) if len(resultStack) > 0 else [] arity = len(s1) if len(p.args) > 1: occurenceIndicator = p.args[1] if (occurenceIndicator == '?' and arity in (0,1) ) or \ (occurenceIndicator == '+' and arity >= 1) or \ (occurenceIndicator == '*'): result = True elif arity == 1: result = True if result and len(p.args) > 0: t = p.args[0] for x in s1: if isinstance(t, QNameDef): if t.namespaceURI == XbrlConst.xsd: tType = { "integer": _INT_TYPES, "string": _STR_BASE, "decimal": Decimal, "double": float, "float": float, "boolean": bool, "QName": QName, "anyURI": AnyURI, "date": DateTime, "dateTime": DateTime, }.get(t.localName) if tType: result = isinstance(x, tType) if result and tType == DateTime: result = x.dateOnly == (t.localName == "date") elif isinstance(t, OperationDef): if t.name == "element": if isinstance(x,ModelObject): if len(t.args) >= 1: qn = t.args[0] if qn== '*' or (isinstance(qn,QNameDef) and qn == x): result = True if len(t.args) >= 2 and isinstance(t.args[1],QNameDef): modelXbrl = x.modelDocument.modelXbrl modelConcept = modelXbrl.qnameConcepts.get(qname(x)) if not modelConcept.instanceOfType(t.args[1]): result = False else: result = False # elif t.name == "item" comes here and result stays True if not result: break elif op == 'sequence': result = self.evaluate(p.args, contextItem=contextItem) elif op == 'predicate': result = self.predicate(p, resultStack.pop()) if len(resultStack) > 0 else [] elif op in FORSOMEEVERY_OPS: # for, some, every result = [] self.evaluateRangeVars(op, p.args[0], p.args[1:], contextItem, result) elif op == 'if': test = self.effectiveBooleanValue( p, self.evaluate(p.args[0].expr[0], contextItem=contextItem) ) result = self.evaluate(p.args[1 if test else 2].args, contextItem=contextItem) elif op == '.': result = contextItem elif op == '..': result = XmlUtil.parent(contextItem) elif op in PATH_OPS: if op in ('rootChild', 'rootDescendant'): # fix up for multi-instance resultStack.append( [self.inputXbrlInstance.xmlDocument,] ) op = '/' if op == 'rootChild' else '//' # contains QNameDefs and predicates if len(resultStack) > 0: innerFocusNodes = resultStack.pop() else: innerFocusNodes = contextItem navSequence = [] for innerFocusNode in self.flattenSequence(innerFocusNodes): navSequence += self.evaluate(p.args, contextItem=innerFocusNode, parentOp=op) result = self.documentOrderedNodes(self.flattenSequence(navSequence)) elif isinstance(p,ProgHeader): self.progHeader = p if p.traceType not in (Trace.MESSAGE, Trace.CUSTOM_FUNCTION): self.traceType = p.traceType setProgHeader = True if result is not None: # note: result can be False which gets appended to resultStack resultStack.append( self.flattenSequence( result ) ) if setProgHeader: self.progHeader = None return resultStack
def evaluate(self, exprStack, contextItem=None, resultStack=None, parentOp=None): if resultStack is None: resultStack = [] if contextItem is None: contextItem = self.contextItem setProgHeader = False for p in exprStack: result = None if isinstance(p, (str, int, float)): result = p elif isinstance(p, VariableRef): if p.name in self.inScopeVars: result = self.inScopeVars[p.name] elif isinstance(p, QNameDef): # step axis operation if len(resultStack) == 0 or not self.isNodeSequence( resultStack[-1]): resultStack.append([ contextItem, ]) result = self.stepAxis(parentOp, p, resultStack.pop()) elif isinstance(p, OperationDef): op = p.name if isinstance(op, QNameDef): # function call args = self.evaluate(p.args, contextItem=contextItem) ns = op.namespaceURI localname = op.localName try: from arelle import (FunctionXs, FunctionFn, FunctionXfi, FunctionCustom) if op in self.modelXbrl.modelCustomFunctionSignatures: result = FunctionCustom.call( self, p, op, contextItem, args) elif op.unprefixed and localname in { 'attribute', 'comment', 'document-node', 'element', 'item', 'node', 'processing-instruction', 'schema-attribute', 'schema-element', 'text' }: # step axis operation if len(resultStack ) == 0 or not self.isNodeSequence( resultStack[-1]): if isinstance(contextItem, (tuple, list)): resultStack.append(contextItem) else: resultStack.append([ contextItem, ]) result = self.stepAxis(parentOp, p, resultStack.pop()) elif op.unprefixed or ns == XbrlConst.fn: result = FunctionFn.call(self, p, localname, contextItem, args) elif ns == XbrlConst.xfi or ns == XbrlConst.xff: result = FunctionXfi.call(self, p, localname, args) elif ns == XbrlConst.xsd: result = FunctionXs.call(self, p, localname, args) else: raise XPathException( p, 'err:XPST0017', _('Function call not identified.')) except FunctionNumArgs: raise XPathException( p, 'err:XPST0017', _('Number of arguments do not match signature arity.' )) except FunctionArgType as err: raise XPathException( p, 'err:XPTY0004', _('Argument {0} does not match expected type {1}.' ).format(err.argNum, err.expectedType)) except FunctionNotAvailable: raise XPathException( p, 'arelle:functDeferred', _('Function {0} is not available in this build.'). format(str(op))) elif op in { '+', '-', '*', 'div', 'idiv', 'mod', 'to', 'gt', 'ge', 'eq', 'ne', 'lt', 'le' }: # binary arithmetic operations and value comparisons s1 = self.atomize( p, resultStack.pop()) if len(resultStack) > 0 else [] s2 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem)) # value comparisons if len(s1) > 1 or len(s2) > 1: raise XPathException( p, 'err:XPTY0004', _("Value operation '{0}' sequence length error"). format(op)) if len(s1) == 0 or len(s2) == 0: result = [] else: op1 = s1[0] op2 = s2[0] from arelle.FunctionUtil import (testTypeCompatiblity) testTypeCompatiblity(self, p, op, op1, op2) if op == '+': result = op1 + op2 elif op == '-': result = op1 - op2 elif op == '*': result = op1 * op2 elif op in ('div', 'idiv', "mod"): try: if op == 'div': result = op1 / op2 elif op == 'idiv': result = op1 // op2 elif op == 'mod': result = op1 % op2 except ZeroDivisionError: raise XPathException( p, 'err:FOAR0001', _('Attempt to divide by zero: {0} {1} {2}.' ).format(op1, op, op2)) elif op == 'ge': result = op1 >= op2 elif op == 'gt': result = op1 > op2 elif op == 'le': result = op1 <= op2 elif op == 'lt': result = op1 < op2 elif op == 'eq': result = op1 == op2 elif op == 'ne': result = op1 != op2 elif op == 'to': result = range(int(op1), int(op2) + 1) elif op in {'>', '>=', '=', '!=', '<', '<='}: # general comparisons s1 = self.atomize( p, resultStack.pop()) if len(resultStack) > 0 else [] s2 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem)) result = [] for op1 in s1: for op2 in s2: if op == '>=': result = op1 >= op2 elif op == '>': result = op1 > op2 elif op == '<=': result = op1 <= op2 elif op == '<': result = op1 < op2 elif op == '=': result = op1 == op2 elif op == '!=': result = op1 != op2 if result: break if result: break elif op in {'is', '>>', '<<'}: # node comparisons s1 = resultStack.pop() if len(resultStack) > 0 else [] s2 = self.evaluate(p.args, contextItem=contextItem) if len(s1) > 1 or len(s2) > 1 or not self.isNodeSequence( s1) or not self.isNodeSequence(s2[0]): raise XPathException( p, 'err:XPTY0004', _('Node comparison sequence error')) if len(s1) == 0 or len(s2[0]) == 0: result = [] else: n1 = s1[0] if isinstance(n1, ModelObject.ModelObject): n1 = n1.element n2 = s2[0][0] if isinstance(n2, ModelObject.ModelObject): n2 = n2.element result = False for op1 in s1: for op2 in s2: if op == 'is': result = n1 == n2 elif op == '>>': result = op1 > op2 elif op == '<<': result = op1 <= op2 if result: break elif op in {'intersect', 'except', 'union', '|'}: # node comparisons s1 = resultStack.pop() if len(resultStack) > 0 else [] s2 = self.flattenSequence( self.evaluate(p.args, contextItem=contextItem)) if not self.isNodeSequence(s1) or not self.isNodeSequence( s2): raise XPathException( p, 'err:XPTY0004', _('Node operation sequence error')) set1 = set(s1) set2 = set(s2) if op == 'intersect': resultset = set1 & set2 elif op == 'except': resultset = set1 - set2 elif op == 'union' or op == '|': resultset = set1 | set2 # convert to a list in document order result = self.documentOrderedNodes(resultset) elif op in {'and', 'or'}: # general comparisons if len(resultStack) == 0: result = [] else: op1 = self.effectiveBooleanValue(p, resultStack.pop()) op2 = self.effectiveBooleanValue( p, self.evaluate(p.args, contextItem=contextItem)) result = False if op == 'and': result = op1 and op2 elif op == 'or': result = op1 or op2 elif op in {'u+', 'u-'}: s1 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem)) if len(s1) > 1: raise XPathException( p, 'err:XPTY0004', _('Unary expression sequence length error')) if len(s1) == 0: result = [] else: op1 = s1[0] if op == 'u+': result = op1 elif op == 'u-': result = -op1 elif op == 'instance': result = False s1 = self.flattenSequence( resultStack.pop()) if len(resultStack) > 0 else [] arity = len(s1) if isinstance(s1, ModelObject.ModelObject): s1 = s1.element if len(p.args) > 1: occurenceIndicator = p.args[1] if (occurenceIndicator == '?' and arity in (0,1) ) or \ (occurenceIndicator == '+' and arity >= 1) or \ (occurenceIndicator == '*'): result = True elif arity == 1: result = True if result and len(p.args) > 0: t = p.args[0] for x in s1: if isinstance(t, QNameDef): if t.namespaceURI == XbrlConst.xsd: type = { "integer": int, "string": str, "decimal": float, "double": float, "float": float, "boolean": bool, "QName": QName, "anyURI": AnyURI, "date": DateTime, "dateTime": DateTime, }.get(t.localName) if type: result = isinstance(x, type) if result and type == DateTime: result = x.dateOnly == ( t.localName == "date") elif isinstance(t, OperationDef): if t.name == "element" and isinstance( x, xml.dom.Node): if len(t.args) >= 1: qn = t.args[0] if qn == '*' or (isinstance( qn, QNameDef) and qn == x): result = True if len(t.args) >= 2 and isinstance( t.args[1], QNameDef): modelXbrl = x.ownerDocument.modelDocument.modelXbrl modelConcept = modelXbrl.qnameConcepts.get( qname(x)) if not modelConcept.instanceOfType( t.args[1]): result = False if not result: break elif op == 'sequence': result = self.evaluate(p.args, contextItem=contextItem) elif op == 'predicate': result = self.predicate(p, resultStack.pop()) elif op in {'for', 'some', 'every'}: # for, some, every result = [] self.evaluateRangeVars(op, p.args[0], p.args[1:], contextItem, result) elif op == 'if': test = self.effectiveBooleanValue( p, self.evaluate(p.args[0].expr[0], contextItem=contextItem)) result = self.evaluate(p.args[1 if test else 2].args, contextItem=contextItem) elif op == '.': result = contextItem elif op == '..': result = XmlUtil.parent(contextItem.element if isinstance( contextItem, ModelObject.ModelObject) else contextItem) elif op in ('/', '//', 'rootChild', 'rootDescendant'): if op in ('rootChild', 'rootDescendant'): # fix up for multi-instance resultStack.append([ self.inputXbrlInstance.xmlDocument, ]) op = '/' if op == 'rootChild' else '//' # contains QNameDefs and predicates if len(resultStack) > 0: innerFocusNodes = resultStack.pop() else: innerFocusNodes = contextItem navSequence = [] for innerFocusNode in self.flattenSequence( innerFocusNodes): self.evaluate(p.args, contextItem=innerFocusNode, resultStack=navSequence, parentOp=op) result = self.documentOrderedNodes( self.flattenSequence(navSequence)) elif isinstance(p, ProgHeader): self.progHeader = p from arelle.ModelFormulaObject import Trace if p.traceType not in (Trace.MESSAGE, Trace.CUSTOM_FUNCTION): self.traceType = p.traceType setProgHeader = True if result is not None: # note: result can be False which gets appended to resultStack resultStack.append(self.flattenSequence(result)) if setProgHeader: self.progHeader = None return resultStack
def evaluate(self, exprStack, contextItem=None, resultStack=None, parentOp=None): if resultStack is None: resultStack = [] if contextItem is None: contextItem = self.contextItem setProgHeader = False for p in exprStack: result = None if isinstance(p,(str,int,float)): result = p elif isinstance(p,VariableRef): if p.name in self.inScopeVars: result = self.inScopeVars[p.name] elif isinstance(p,QNameDef): # step axis operation if len(resultStack) == 0 or not self.isNodeSequence(resultStack[-1]): resultStack.append( [ contextItem, ] ) result = self.stepAxis(parentOp, p, resultStack.pop() ) elif isinstance(p,OperationDef): op = p.name if isinstance(op, QNameDef): # function call args = self.evaluate(p.args, contextItem=contextItem) ns = op.namespaceURI; localname = op.localName try: from arelle import (FunctionXs, FunctionFn, FunctionXfi, FunctionIxt, FunctionCustom) if op in self.modelXbrl.modelCustomFunctionSignatures: result = FunctionCustom.call(self, p, op, contextItem, args) elif op.unprefixed and localname in {'attribute', 'comment', 'document-node', 'element', 'item', 'node', 'processing-instruction', 'schema-attribute', 'schema-element', 'text'}: # step axis operation if len(resultStack) == 0 or not self.isNodeSequence(resultStack[-1]): if isinstance(contextItem, (tuple,list)): resultStack.append( contextItem ) else: resultStack.append( [ contextItem, ] ) result = self.stepAxis(parentOp, p, resultStack.pop() ) elif op.unprefixed or ns == XbrlConst.fn: result = FunctionFn.call(self, p, localname, contextItem, args) elif ns == XbrlConst.xfi or ns == XbrlConst.xff: result = FunctionXfi.call(self, p, localname, args) elif ns == XbrlConst.xsd: result = FunctionXs.call(self, p, localname, args) elif ns.startswith("http://www.xbrl.org/inlineXBRL/transformation"): result = FunctionIxt.call(self, p, localname, args) else: raise XPathException(p, 'err:XPST0017', _('Function call not identified.')) except FunctionNumArgs: raise XPathException(p, 'err:XPST0017', _('Number of arguments do not match signature arity.')) except FunctionArgType as err: raise XPathException(p, err.errCode, _('Argument {0} does not match expected type {1}.') .format(err.argNum, err.expectedType)) except FunctionNotAvailable: raise XPathException(p, 'arelle:functDeferred', _('Function {0} is not available in this build.') .format(str(op))) elif op in {'+', '-', '*', 'div', 'idiv', 'mod', 'to', 'gt', 'ge', 'eq', 'ne', 'lt', 'le'}: # binary arithmetic operations and value comparisons s1 = self.atomize( p, resultStack.pop() ) if len(resultStack) > 0 else [] s2 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem) ) # value comparisons if len(s1) > 1 or len(s2) > 1: raise XPathException(p, 'err:XPTY0004', _("Value operation '{0}' sequence length error").format(op)) if len(s1) == 0 or len(s2) == 0: result = [] else: op1 = s1[0] op2 = s2[0] from arelle.FunctionUtil import (testTypeCompatiblity) testTypeCompatiblity( self, p, op, op1, op2 ) if op == '+': result = op1 + op2 elif op == '-': result = op1 - op2 elif op == '*': result = op1 * op2 elif op in ('div', 'idiv', "mod"): try: if op == 'div': result = op1 / op2 elif op == 'idiv': result = op1 // op2 elif op == 'mod': result = op1 % op2 except ZeroDivisionError: raise XPathException(p, 'err:FOAR0001', _('Attempt to divide by zero: {0} {1} {2}.') .format(op1, op, op2)) elif op == 'ge': result = op1 >= op2 elif op == 'gt': result = op1 > op2 elif op == 'le': result = op1 <= op2 elif op == 'lt': result = op1 < op2 elif op == 'eq': result = op1 == op2 elif op == 'ne': result = op1 != op2 elif op == 'to': result = range( int(op1), int(op2) + 1 ) elif op in {'>', '>=', '=', '!=', '<', '<='}: # general comparisons s1 = self.atomize( p, resultStack.pop() ) if len(resultStack) > 0 else [] s2 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem) ) result = []; for op1 in s1: for op2 in s2: if op == '>=': result = op1 >= op2 elif op == '>': result = op1 > op2 elif op == '<=': result = op1 <= op2 elif op == '<': result = op1 < op2 elif op == '=': result = op1 == op2 elif op == '!=': result = op1 != op2 if result: break if result: break elif op in {'is', '>>', '<<'}: # node comparisons s1 = resultStack.pop() if len(resultStack) > 0 else [] s2 = self.evaluate(p.args, contextItem=contextItem) if len(s1) > 1 or len(s2) > 1 or not self.isNodeSequence(s1) or not self.isNodeSequence(s2[0]): raise XPathException(p, 'err:XPTY0004', _('Node comparison sequence error')) if len(s1) == 0 or len(s2[0]) == 0: result = [] else: n1 = s1[0] n2 = s2[0][0] result = False; for op1 in s1: for op2 in s2: if op == 'is': result = n1 == n2 elif op == '>>': result = op1 > op2 elif op == '<<': result = op1 <= op2 if result: break elif op in {'intersect','except','union','|'}: # node comparisons s1 = resultStack.pop() if len(resultStack) > 0 else [] s2 = self.flattenSequence(self.evaluate(p.args, contextItem=contextItem)) if not self.isNodeSequence(s1) or not self.isNodeSequence(s2): raise XPathException(p, 'err:XPTY0004', _('Node operation sequence error')) set1 = set(s1) set2 = set(s2) if op == 'intersect': resultset = set1 & set2 elif op == 'except': resultset = set1 - set2 elif op == 'union' or op == '|': resultset = set1 | set2 # convert to a list in document order result = self.documentOrderedNodes(resultset) elif op in {'and', 'or'}: # general comparisons if len(resultStack) == 0: result = [] else: op1 = self.effectiveBooleanValue( p, resultStack.pop() ) op2 = self.effectiveBooleanValue( p, self.evaluate(p.args, contextItem=contextItem) ) result = False; if op == 'and': result = op1 and op2 elif op == 'or': result = op1 or op2 elif op in {'u+', 'u-'}: s1 = self.atomize( p, self.evaluate(p.args, contextItem=contextItem) ) if len(s1) > 1: raise XPathException(p, 'err:XPTY0004', _('Unary expression sequence length error')) if len(s1) == 0: result = [] else: op1 = s1[0] if op == 'u+': result = op1 elif op == 'u-': result = -op1 elif op == 'instance': result = False s1 = self.flattenSequence( resultStack.pop() ) if len(resultStack) > 0 else [] arity = len(s1) if len(p.args) > 1: occurenceIndicator = p.args[1] if (occurenceIndicator == '?' and arity in (0,1) ) or \ (occurenceIndicator == '+' and arity >= 1) or \ (occurenceIndicator == '*'): result = True elif arity == 1: result = True if result and len(p.args) > 0: t = p.args[0] for x in s1: if isinstance(t, QNameDef): if t.namespaceURI == XbrlConst.xsd: type = { "integer": int, "string": str, "decimal": float, "double": float, "float": float, "boolean": bool, "QName": QName, "anyURI": AnyURI, "date": DateTime, "dateTime": DateTime, }.get(t.localName) if type: result = isinstance(x, type) if result and type == DateTime: result = x.dateOnly == (t.localName == "date") elif isinstance(t, OperationDef): if t.name == "element" and isinstance(x,ModelObject): if len(t.args) >= 1: qn = t.args[0] if qn== '*' or (isinstance(qn,QNameDef) and qn == x): result = True if len(t.args) >= 2 and isinstance(t.args[1],QNameDef): modelXbrl = x.modelDocument.modelXbrl modelConcept = modelXbrl.qnameConcepts.get(qname(x)) if not modelConcept.instanceOfType(t.args[1]): result = False if not result: break elif op == 'sequence': result = self.evaluate(p.args, contextItem=contextItem) elif op == 'predicate': result = self.predicate(p, resultStack.pop()) elif op in {'for','some','every'}: # for, some, every result = [] self.evaluateRangeVars(op, p.args[0], p.args[1:], contextItem, result) elif op == 'if': test = self.effectiveBooleanValue( p, self.evaluate(p.args[0].expr[0], contextItem=contextItem) ) result = self.evaluate(p.args[1 if test else 2].args, contextItem=contextItem) elif op == '.': result = contextItem elif op == '..': result = XmlUtil.parent(contextItem) elif op in ('/', '//', 'rootChild', 'rootDescendant'): if op in ('rootChild', 'rootDescendant'): # fix up for multi-instance resultStack.append( [self.inputXbrlInstance.xmlDocument,] ) op = '/' if op == 'rootChild' else '//' # contains QNameDefs and predicates if len(resultStack) > 0: innerFocusNodes = resultStack.pop() else: innerFocusNodes = contextItem navSequence = [] for innerFocusNode in self.flattenSequence(innerFocusNodes): navSequence += self.evaluate(p.args, contextItem=innerFocusNode, parentOp=op) result = self.documentOrderedNodes(self.flattenSequence(navSequence)) elif isinstance(p,ProgHeader): self.progHeader = p from arelle.ModelFormulaObject import Trace if p.traceType not in (Trace.MESSAGE, Trace.CUSTOM_FUNCTION): self.traceType = p.traceType setProgHeader = True if result is not None: # note: result can be False which gets appended to resultStack resultStack.append( self.flattenSequence( result ) ) if setProgHeader: self.progHeader = None return resultStack
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 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: validate(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 isinstance(node, (ModelObject, etree._ElementTree)): targetNodes = XmlUtil.children(node, ns, localname) 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": targetNodes = [XmlUtil.text(node)] # 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
if not result: break elif op == u'sequence': result = self.evaluate(p.args, contextItem=contextItem) elif op == u'predicate': result = self.predicate(p, resultStack.pop()) if len(resultStack) > 0 else [] elif op in FORSOMEEVERY_OPS: # for, some, every result = [] self.evaluateRangeVars(op, p.args[0], p.args[1:], contextItem, result) elif op == u'if': test = self.effectiveBooleanValue( p, self.evaluate(p.args[0].expr[0], contextItem=contextItem) ) result = self.evaluate(p.args[1 if test else 2].args, contextItem=contextItem) elif op == u'.': result = contextItem elif op == u'..': result = XmlUtil.parent(contextItem) elif op in PATH_OPS: if op in (u'rootChild', u'rootDescendant'): # fix up for multi-instance resultStack.append( [self.inputXbrlInstance.xmlDocument,] ) op = u'/' if op == u'rootChild' else u'//' # contains QNameDefs and predicates if len(resultStack) > 0: innerFocusNodes = resultStack.pop() else: innerFocusNodes = contextItem navSequence = [] for innerFocusNode in self.flattenSequence(innerFocusNodes): navSequence += self.evaluate(p.args, contextItem=innerFocusNode, parentOp=op) result = self.documentOrderedNodes(self.flattenSequence(navSequence)) elif isinstance(p,ProgHeader):