コード例 #1
0
ファイル: XPathContext.py プロジェクト: 8maki/Arelle
 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
コード例 #2
0
 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
コード例 #3
0
ファイル: XPathContext.py プロジェクト: Bourne-Law/Arelle
 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
コード例 #4
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
コード例 #5
0
 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
コード例 #6
0
ファイル: XPathContext.py プロジェクト: Arelle/Arelle
 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
コード例 #7
0
ファイル: XPathContext.py プロジェクト: Arelle/Arelle
 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
コード例 #8
0
 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
コード例 #9
0
ファイル: XPathContext.py プロジェクト: marado/Arelle
 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
コード例 #10
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
             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
コード例 #11
0
                 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):