Example #1
0
 def evaluateAtomicValue(self, exprStack, type, contextItem=None):
     if exprStack and len(exprStack) > 0 and isinstance(
             exprStack[0], ProgHeader):
         progHeader = exprStack[0]
         result = self.atomize(
             progHeader, self.evaluate(exprStack, contextItem=contextItem))
         if isinstance(type, QName) and type.namespaceURI == XbrlConst.xsd:
             type = "xs:" + type.localName
         if isinstance(type, str):
             prefix, sep, localName = type.rpartition(':')
             if prefix == 'xs':
                 if localName.endswith('*'): localName = localName[:-1]
                 if isinstance(result, (tuple, list, set)):
                     from arelle import (FunctionXs)
                     if type.endswith('*'):
                         return [
                             FunctionXs.call(self, progHeader, localName,
                                             (r, )) for r in result
                         ]
                     elif len(result) > 0:
                         return FunctionXs.call(self, progHeader, localName,
                                                (result[0], ))
             elif localName.startswith("item()"):
                 return result  # can be any type
         else:  # no conversion
             if len(result) == 0: return None
             elif len(result) == 1: return result[0]
             else: return result
     return None
Example #2
0
def concat(xc, p, contextItem, args):
    if len(args) < 2: raise XPathContext.FunctionNumArgs()
    atomizedArgs = []
    for i in range(len(args)):
        item = anytypeArg(xc, args, i, "xs:anyAtomicType?")
        if item != ():
            atomizedArgs.append( FunctionXs.xsString( xc, p, xc.atomize(p, item) ) )
    return ''.join(atomizedArgs)
Example #3
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 == u'*' and parentOp in (u'/', u'//')): # 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.unprefixed and localname in set([u'attribute', u'comment', u'document-node', u'element', 
                        u'item', u'node', u'processing-instruction', u'schema-attribute', u'schema-element', u'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.ixtNamespaceURIs:
                         result = FunctionIxt.call(self, p, localname, args)
                     else:
                         raise XPathException(p, u'err:XPST0017', _(u'Function call not identified: {0}.').format(op))
                 except FunctionNumArgs, err:
                     raise XPathException(p, err.errCode, u"{}: {}".format(err.errText, op))
                 except FunctionArgType, err:
                     raise XPathException(p, err.errCode, _(u'Argument {0} does not match expected type {1} for {2} {3}.')
                                          .format(err.argNum, err.expectedType, op, err.foundObject))
                 except FunctionNotAvailable:
                     raise XPathException(p, u'err:XPST0017', _(u'Function named {0} does not have a custom or built-in implementation.').format(op))
Example #4
0
def string(xc, p, contextItem, args):
    if len(args) > 1: raise XPathContext.FunctionNumArgs()
    item = anytypeArg(xc, args, 0, "item()?", missingArgFallback=contextItem)
    if item == (): 
        return ''
    if isinstance(item, ModelObject) and getattr(item,"xValid", 0) == VALID_NO_CONTENT:
        x = item.stringValue # represents inner text of this and all subelements
    else:
        x = xc.atomize(p, item)
    return FunctionXs.xsString( xc, p, x ) 
Example #5
0
def string(xc, p, contextItem, args):
    if len(args) > 1: raise XPathContext.FunctionNumArgs()
    x = atomicArg(xc,
                  p,
                  args,
                  0,
                  "item()?",
                  missingArgFallback=contextItem,
                  emptyFallback='')
    return FunctionXs.xsString(xc, p, x)
Example #6
0
def string(xc, p, contextItem, args):
    if len(args) > 1: raise XPathContext.FunctionNumArgs()
    item = anytypeArg(xc, args, 0, "item()?", missingArgFallback=contextItem)
    if item == ():
        return ''
    if isinstance(item, ModelObject) and getattr(item, "xValid",
                                                 0) == VALID_NO_CONTENT:
        x = item.stringValue  # represents inner text of this and all subelements
    else:
        x = xc.atomize(p, item)
    return FunctionXs.xsString(xc, p, x)
Example #7
0
 def evaluateAtomicValue(self, exprStack, type, contextItem=None):
     if exprStack and len(exprStack) > 0 and isinstance(exprStack[0], ProgHeader):
         progHeader = exprStack[0]
         result = self.atomize( progHeader, self.evaluate( exprStack, contextItem=contextItem ) )
         if isinstance(type, QName) and type.namespaceURI == XbrlConst.xsd:
             type = "xs:" + type.localName
         if isinstance(type,str):
             prefix,sep,localName = type.partition(':')
             if prefix == 'xs':
                 if localName.endswith('*'): localName = localName[:-1]
                 if isinstance(result, (tuple,list,set)):
                     from arelle import (FunctionXs)
                     if type.endswith('*'):
                         return[FunctionXs.call(self,progHeader,localName,(r,)) for r in result]
                     elif len(result) > 0:
                         return FunctionXs.call(self,progHeader,localName,(result[0],))
         else: # no conversion
             if len(result) == 0: return None
             elif len(result) == 1: return result[0]
             else: return result
     return None
Example #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, 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
Example #9
0
def string(xc, p, contextItem, args):
    if len(args) > 1: raise XPathContext.FunctionNumArgs()
    x = atomicArg(xc, p, args, 0, "item()?", missingArgFallback=contextItem, emptyFallback='')
    return FunctionXs.xsString( xc, p, x ) 
Example #10
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
Example #11
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
Example #12
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, 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
Example #13
0
def validate(val):
    formulaOptions = val.modelXbrl.modelManager.formulaOptions
    XPathParser.initializeParser(val)
    val.modelXbrl.modelManager.showStatus(_("Compiling formulae"))
    initialErrorCount = val.modelXbrl.logCountErr

    # global parameter names
    parameterQnames = set()
    instanceQnames = set()
    parameterDependencies = {}
    instanceDependencies = defaultdict(
        set)  # None-key entries are non-formula dependencies
    dependencyResolvedParameters = set()
    orderedParameters = []
    orderedInstances = []
    for paramQname, modelParameter in val.modelXbrl.qnameParameters.items():
        if isinstance(modelParameter, ModelParameter):
            modelParameter.compile()
            parameterDependencies[paramQname] = modelParameter.variableRefs()
            parameterQnames.add(paramQname)
            if isinstance(modelParameter, ModelInstance):
                instanceQnames.add(paramQname)
            # duplicates checked on loading modelDocument

    #resolve dependencies
    resolvedAParameter = True
    while (resolvedAParameter):
        resolvedAParameter = False
        for paramQname in parameterQnames:
            if paramQname not in dependencyResolvedParameters and \
               len(parameterDependencies[paramQname] - dependencyResolvedParameters) == 0:
                dependencyResolvedParameters.add(paramQname)
                orderedParameters.append(paramQname)
                resolvedAParameter = True
    # anything unresolved?
    for paramQname in parameterQnames:
        if paramQname not in dependencyResolvedParameters:
            circularOrUndefDependencies = parameterDependencies[
                paramQname] - dependencyResolvedParameters
            undefinedVars = circularOrUndefDependencies - parameterQnames
            paramsCircularDep = circularOrUndefDependencies - undefinedVars
            if len(undefinedVars) > 0:
                val.modelXbrl.error(
                    _("Undefined dependencies in parameter {0}, to names {1}"
                      ).format(paramQname, ", ".join(
                          (str(v) for v in undefinedVars))), "err",
                    "xbrlve:unresolvedDependency")
            if len(paramsCircularDep) > 0:
                val.modelXbrl.error(
                    _("Cyclic dependencies in parameter {0}, to names {1}"
                      ).format(paramQname, ", ".join(
                          (str(d) for d in paramsCircularDep))), "err",
                    "xbrlve:parameterCyclicDependencies")

    for custFnSig in val.modelXbrl.modelCustomFunctionSignatures.values():
        custFnQname = custFnSig.qname
        if custFnQname.namespaceURI == "XbrlConst.xfi":
            val.modelXbrl.error(
                _("Custom function {0} has namespace reserved for functions in the function registry {1}"
                  ).format(str(custFnQname), custFnQname.namespaceURI), "err",
                "xbrlve:noProhibitedNamespaceForCustomFunction")
        # any custom function implementations?
        for modelRel in val.modelXbrl.relationshipSet(
                XbrlConst.functionImplementation).fromModelObject(custFnSig):
            custFnImpl = modelRel.toModelObject
            custFnSig.customFunctionImplementation = custFnImpl
            if len(custFnImpl.inputNames) != len(custFnSig.inputTypes):
                val.modelXbrl.error(
                    _("Custom function {0} signature has {1} parameters but implementation has {2}, must be matching"
                      ).format(str(custFnQname), len(custFnSig.inputTypes),
                               len(custFnImpl.inputNames)), "err",
                    "xbrlcfie:inputMismatch")

    for custFnImpl in val.modelXbrl.modelCustomFunctionImplementations:
        if not val.modelXbrl.relationshipSet(
                XbrlConst.functionImplementation).toModelObject(custFnImpl):
            val.modelXbrl.error(
                _("Custom function implementation {0} has no relationship from any custom function signature"
                  ).format(custFnImpl.xlinkLabel), "err",
                "xbrlcfie:missingCFIRelationship")
        custFnImpl.compile()

    # xpathContext is needed for filter setup for expressions such as aspect cover filter
    # determine parameter values
    xpathContext = XPathContext.create(val.modelXbrl)
    for paramQname in orderedParameters:
        if not isinstance(modelParameter, ModelInstance):
            modelParameter = val.modelXbrl.qnameParameters[paramQname]
            asType = modelParameter.asType
            asLocalName = asType.localName if asType else "string"
            try:
                if val.parameters and paramQname in val.parameters:
                    paramDataType, paramValue = val.parameters[paramQname]
                    typeLocalName = paramDataType.localName if paramDataType else "string"
                    value = FunctionXs.call(xpathContext, None, typeLocalName,
                                            [paramValue])
                    result = FunctionXs.call(xpathContext, None, asLocalName,
                                             [value])
                    if formulaOptions.traceParameterInputValue:
                        val.modelXbrl.error(
                            _("Parameter {0} input {1}").format(
                                paramQname, result), "info", "formula:trace")
                else:
                    result = modelParameter.evaluate(xpathContext, asType)
                    if formulaOptions.traceParameterExpressionResult:
                        val.modelXbrl.error(
                            _("Parameter {0} result {1}").format(
                                paramQname, result), "info", "formula:trace")
                xpathContext.inScopeVars[
                    paramQname] = result  # make visible to subsequent parameter expression
            except XPathContext.XPathException as err:
                val.modelXbrl.error(
                    _("Parameter \n{0} \nException: \n{1}").format(
                        paramQname, err.message), "err",
                    "xbrlve:parameterTypeMismatch"
                    if err.code == "err:FORG0001" else err.code)

    produceOutputXbrlInstance = False
    instanceProducingVariableSets = defaultdict(list)

    for modelVariableSet in val.modelXbrl.modelVariableSets:
        varSetInstanceDependencies = set()
        if isinstance(modelVariableSet, ModelFormula):
            instanceQname = None
            for modelRel in val.modelXbrl.relationshipSet(
                    XbrlConst.formulaInstance).fromModelObject(
                        modelVariableSet):
                instance = modelRel.toModelObject
                if isinstance(instance, ModelInstance):
                    if instanceQname is None:
                        instanceQname = instance.qname
                    else:
                        val.modelXbrl.error(
                            _("Multiple output instances for formula {0}, to names {1}, {2}"
                              ).format(modelVariableSet.xlinkLabel,
                                       instanceQname, instance.qname), "info",
                            "arelle:multipleOutputInstances")
            if instanceQname is None:
                instanceQname = XbrlConst.qnStandardOutputInstance
                instanceQnames.add(instanceQname)
            modelVariableSet.outputInstanceQname = instanceQname
            if val.validateSBRNL:
                val.modelXbrl.error(
                    _("Formula linkbase {0} formula:formula {1} is not allowed"
                      ).format(
                          os.path.basename(modelVariableSet.modelDocument.uri),
                          modelVariableSet.xlinkLabel), "err",
                    "SBR.NL.2.3.9.03")
        else:
            instanceQname = None
            modelVariableSet.countSatisfied = 0
            modelVariableSet.countNotSatisfied = 0
            checkValidationMessages(val, modelVariableSet)
        instanceProducingVariableSets[instanceQname].append(modelVariableSet)
        modelVariableSet.outputInstanceQname = instanceQname
        if modelVariableSet.aspectModel not in ("non-dimensional",
                                                "dimensional"):
            val.modelXbrl.error(
                _("Variable set {0}, aspect model {1} not recognized").format(
                    modelVariableSet.xlinkLabel, modelVariableSet.aspectModel),
                "err", "xbrlve:unknownAspectModel")
        modelVariableSet.compile()
        modelVariableSet.hasConsistencyAssertion = False

        #determine dependencies within variable sets
        nameVariables = {}
        qnameRels = {}
        definedNamesSet = set()
        for modelRel in val.modelXbrl.relationshipSet(
                XbrlConst.variableSet).fromModelObject(modelVariableSet):
            varqname = modelRel.variableQname
            if varqname:
                qnameRels[varqname] = modelRel
                toVariable = modelRel.toModelObject
                if varqname not in definedNamesSet:
                    definedNamesSet.add(varqname)
                if varqname not in nameVariables:
                    nameVariables[varqname] = toVariable
                elif nameVariables[varqname] != toVariable:
                    val.modelXbrl.error(
                        _("Multiple variables named {1} in variable set {0}"
                          ).format(modelVariableSet.xlinkLabel, varqname),
                        "err", "xbrlve:duplicateVariableNames")
                fromInstanceQnames = None
                for instRel in val.modelXbrl.relationshipSet(
                        XbrlConst.instanceVariable).toModelObject(toVariable):
                    fromInstance = instRel.fromModelObject
                    if isinstance(fromInstance, ModelInstance):
                        fromInstanceQname = fromInstance.qname
                        varSetInstanceDependencies.add(fromInstanceQname)
                        instanceDependencies[instanceQname].add(
                            fromInstanceQname)
                        if fromInstanceQnames is None:
                            fromInstanceQnames = set()
                        fromInstanceQnames.add(fromInstanceQname)
                if fromInstanceQnames is None:
                    varSetInstanceDependencies.add(
                        XbrlConst.qnStandardInputInstance)
                    if instanceQname:
                        instanceDependencies[instanceQname].add(
                            XbrlConst.qnStandardInputInstance)
                toVariable.fromInstanceQnames = fromInstanceQnames
            else:
                val.modelXbrl.error(
                    _("Variables name {1} cannot be determined on arc from {0}"
                      ).format(modelVariableSet.xlinkLabel,
                               modelRel.variablename), "err",
                    "xbrlve:variableNameResolutionFailure")
        definedNamesSet |= parameterQnames

        variableDependencies = {}
        for modelRel in val.modelXbrl.relationshipSet(
                XbrlConst.variableSet).fromModelObject(modelVariableSet):
            variable = modelRel.toModelObject
            if isinstance(
                    variable,
                (ModelParameter,
                 ModelVariable)):  # ignore anything not parameter or variable
                varqname = modelRel.variableQname
                depVars = variable.variableRefs()
                variableDependencies[varqname] = depVars
                if len(depVars
                       ) > 0 and formulaOptions.traceVariablesDependencies:
                    val.modelXbrl.error(
                        _("Variable set {0}, variable {1}, dependences {2}").
                        format(modelVariableSet.xlinkLabel, varqname,
                               depVars), "info", "formula:trace")
                definedNamesSet.add(varqname)
                # check for fallback value variable references
                if isinstance(variable, ModelFactVariable):
                    for depVar in XPathParser.variableReferencesSet(
                            variable.fallbackValueProg, variable.element):
                        if depVar in qnameRels and isinstance(
                                qnameRels[depVar].toModelObject,
                                ModelVariable):
                            val.modelXbrl.error(
                                _("Variable set {0} fallbackValue '{1}' cannot refer to variable {2}"
                                  ).format(modelVariableSet.xlinkLabel,
                                           variable.fallbackValue, depVar),
                                "err",
                                "xbrlve:factVariableReferenceNotAllowed")
                    # check for covering aspect not in variable set aspect model
                    checkFilterAspectModel(val, modelVariableSet,
                                           variable.filterRelationships,
                                           xpathContext)

        orderedNameSet = set()
        orderedNameList = []
        orderedAVariable = True
        while (orderedAVariable):
            orderedAVariable = False
            for varqname, depVars in variableDependencies.items():
                if varqname not in orderedNameSet and len(depVars -
                                                          parameterQnames -
                                                          orderedNameSet) == 0:
                    orderedNameList.append(varqname)
                    orderedNameSet.add(varqname)
                    orderedAVariable = True
                if varqname in instanceQnames:
                    varSetInstanceDependencies.add(varqname)
                    instanceDependencies[instanceQname].add(varqname)
                elif isinstance(nameVariables.get(varqname), ModelInstance):
                    instqname = nameVariables[varqname].qname
                    varSetInstanceDependencies.add(instqname)
                    instanceDependencies[instanceQname].add(instqname)

        # anything unresolved?
        for varqname, depVars in variableDependencies.items():
            if varqname not in orderedNameSet:
                circularOrUndefVars = depVars - parameterQnames - orderedNameSet
                undefinedVars = circularOrUndefVars - definedNamesSet
                varsCircularDep = circularOrUndefVars - undefinedVars
                if len(undefinedVars) > 0:
                    val.modelXbrl.error(
                        _("Undefined variable dependencies in variable st {0}, from variable {1} to {2}"
                          ).format(modelVariableSet.xlinkLabel, varqname,
                                   undefinedVars), "err",
                        "xbrlve:unresolvedDependency")
                if len(varsCircularDep) > 0:
                    val.modelXbrl.error(
                        _("Cyclic dependencies in variable set {0}, from variable {1} to {2}"
                          ).format(modelVariableSet.xlinkLabel, varqname,
                                   varsCircularDep), "err",
                        "xbrlve:cyclicDependencies")

        # check unresolved variable set dependencies
        for varSetDepVarQname in modelVariableSet.variableRefs():
            if varSetDepVarQname not in orderedNameSet and varSetDepVarQname not in parameterQnames:
                val.modelXbrl.error(
                    _("Undefined variable dependency in variable set {0}, {1}"
                      ).format(modelVariableSet.xlinkLabel, varSetDepVarQname),
                    "err", "xbrlve:unresolvedDependency")
            if varSetDepVarQname in instanceQnames:
                varSetInstanceDependencies.add(varSetDepVarQname)
                instanceDependencies[instanceQname].add(varSetDepVarQname)
            elif isinstance(nameVariables.get(varSetDepVarQname),
                            ModelInstance):
                instqname = nameVariables[varSetDepVarQname].qname
                varSetInstanceDependencies.add(instqname)
                instanceDependencies[instanceQname].add(instqname)

        if formulaOptions.traceVariablesOrder:
            val.modelXbrl.error(
                _("Variable set {0}, variables order: {1}").format(
                    modelVariableSet.xlinkLabel, orderedNameList), "info",
                "formula:trace")

        if (formulaOptions.traceVariablesDependencies
                and len(varSetInstanceDependencies) > 0
                and varSetInstanceDependencies !=
            {XbrlConst.qnStandardInputInstance}):
            val.modelXbrl.error(
                _("Variable set {0}, instance dependences {1}").format(
                    modelVariableSet.xlinkLabel, varSetInstanceDependencies),
                "info", "formula:trace")

        modelVariableSet.orderedVariableRelationships = []
        for varqname in orderedNameList:
            if varqname in qnameRels:
                modelVariableSet.orderedVariableRelationships.append(
                    qnameRels[varqname])

        # check existence assertion variable dependencies
        if isinstance(modelVariableSet, ModelExistenceAssertion):
            for depVar in modelVariableSet.variableRefs():
                if depVar in qnameRels and isinstance(
                        qnameRels[depVar].toModelObject, ModelVariable):
                    val.modelXbrl.error(
                        _("Existence Assertion {0}, cannot refer to variable {1}"
                          ).format(modelVariableSet.xlinkLabel, depVar), "err",
                        "xbrleae:variableReferenceNotAllowed")

        # check messages variable dependencies
        checkValidationMessageVariables(val, modelVariableSet, qnameRels)

        # check preconditions
        modelVariableSet.preconditions = []
        for modelRel in val.modelXbrl.relationshipSet(
                XbrlConst.variableSetPrecondition).fromModelObject(
                    modelVariableSet):
            precondition = modelRel.toModelObject
            if isinstance(precondition, ModelPrecondition):
                modelVariableSet.preconditions.append(precondition)

        # check for variable sets referencing fact or general variables
        for modelRel in val.modelXbrl.relationshipSet(
                XbrlConst.variableSetFilter).fromModelObject(modelVariableSet):
            varSetFilter = modelRel.toModelObject
            if modelRel.isCovered:
                val.modelXbrl.error(
                    _("Variable set {0}, filter {1}, cannot be covered"
                      ).format(modelVariableSet.xlinkLabel,
                               varSetFilter.xlinkLabel), "wrn",
                    "arelle:variableSetFilterCovered")
                modelRel._isCovered = False  # block group filter from being able to covere
            for depVar in varSetFilter.variableRefs():
                if depVar in qnameRels and isinstance(
                        qnameRels[depVar].toModelObject, ModelVariable):
                    val.modelXbrl.error(
                        _("Variable set {0}, filter {1}, cannot refer to variable {2}"
                          ).format(modelVariableSet.xlinkLabel,
                                   varSetFilter.xlinkLabel, depVar), "err",
                        "xbrlve:factVariableReferenceNotAllowed")

        # check aspects of formula
        if isinstance(modelVariableSet, ModelFormula):
            checkFormulaRules(val, modelVariableSet, nameVariables)

    # determine instance dependency order
    orderedInstancesSet = set()
    stdInpInst = {XbrlConst.qnStandardInputInstance}
    orderedInstancesList = []
    orderedAnInstance = True
    while (orderedAnInstance):
        orderedAnInstance = False
        for instqname, depInsts in instanceDependencies.items():
            if instqname and instqname not in orderedInstancesSet and len(
                    depInsts - stdInpInst - orderedInstancesSet) == 0:
                orderedInstancesList.append(instqname)
                orderedInstancesSet.add(instqname)
                orderedAnInstance = True
    orderedInstancesList.append(
        None)  # assertions come after all formulas that produce outputs

    # anything unresolved?
    for instqname, depInsts in instanceDependencies.items():
        if instqname not in orderedInstancesSet:
            # can also be satisfied from an input DTS
            missingDependentInstances = depInsts - stdInpInst
            if val.parameters:
                missingDependentInstances -= val.parameters.keys()
            if instqname:
                if missingDependentInstances:
                    val.modelXbrl.error(
                        _("Cyclic dependencies of instance {0} produced by a formula, with variables consuming instances {1}"
                          ).format(instqname, missingDependentInstances),
                        "err", "xbrlvarinste:instanceVariableRecursionCycle")
                elif instqname == XbrlConst.qnStandardOutputInstance:
                    orderedInstancesSet.add(instqname)
                    orderedInstancesList.append(
                        instqname
                    )  # standard output formula, all input dependencies in parameters
            ''' future check?  if instance has no external input or producing formula
            else:
                val.modelXbrl.error(
                    _("Unresolved dependencies of an assertion's variables on instances {0}").format(
                          depInsts - stdInpInst ), 
                    "err", "xbrlvarinste:instanceVariableRecursionCycle")
            '''

    if formulaOptions.traceVariablesOrder and len(orderedInstancesList) > 1:
        val.modelXbrl.error(
            _("Variable instances processing order: {0}").format(
                orderedInstancesList), "info", "formula:trace")

    # linked consistency assertions
    for modelRel in val.modelXbrl.relationshipSet(
            XbrlConst.consistencyAssertionFormula).modelRelationships:
        if modelRel.fromModelObject and modelRel.toModelObject and isinstance(
                modelRel.toModelObject, ModelFormula):
            consisAsser = modelRel.fromModelObject
            consisAsser.countSatisfied = 0
            consisAsser.countNotSatisfied = 0
            if consisAsser.hasProportionalAcceptanceRadius and consisAsser.hasAbsoluteAcceptanceRadius:
                val.modelXbrl.error(
                    _("Consistency assertion {0} has both absolute and proportional acceptance radii"
                      ).format(consisAsser.xlinkLabel), "err",
                    "xbrlcae:acceptanceRadiusConflict")
            consisAsser.orderedVariableRelationships = []
            for consisParamRel in val.modelXbrl.relationshipSet(
                    XbrlConst.consistencyAssertionParameter).fromModelObject(
                        consisAsser):
                if isinstance(consisParamRel.toModelObject, ModelVariable):
                    val.modelXbrl.error(
                        _("Consistency assertion {0} has relationship to a {1} {2}"
                          ).format(
                              consisAsser.xlinkLabel,
                              consisParamRel.toModelObject.element.localName,
                              consisParamRel.toModelObject.xlinkLabel), "err",
                        "xbrlcae:variablesNotAllowed")
                else:
                    consisAsser.orderedVariableRelationships.append(
                        consisParamRel)
            consisAsser.compile()
            modelRel.toModelObject.hasConsistencyAssertion = True

    if initialErrorCount < val.modelXbrl.logCountErr:
        return  # don't try to execute

    # formula output instances
    if instanceQnames:
        schemaRefs = [
            val.modelXbrl.modelDocument.relativeUri(referencedDoc.uri)
            for referencedDoc in
            val.modelXbrl.modelDocument.referencesDocument.keys()
            if referencedDoc.type == ModelDocument.Type.SCHEMA
        ]

    outputXbrlInstance = None
    for instanceQname in instanceQnames:
        if instanceQname == XbrlConst.qnStandardInputInstance:
            continue  # always present the standard way
        if val.parameters and instanceQname in val.parameters:
            namedInstance = val.parameters[instanceQname][1]
        else:  # empty intermediate instance
            uri = val.modelXbrl.modelDocument.filepath[:-4] + "-output-XBRL-instance"
            if instanceQname != XbrlConst.qnStandardOutputInstance:
                uri = uri + "-" + instanceQname.localName
            uri = uri + ".xml"
            namedInstance = ModelXbrl.create(
                val.modelXbrl.modelManager,
                newDocumentType=ModelDocument.Type.INSTANCE,
                url=uri,
                schemaRefs=schemaRefs,
                isEntry=True)
        xpathContext.inScopeVars[instanceQname] = namedInstance
        if instanceQname == XbrlConst.qnStandardOutputInstance:
            outputXbrlInstance = namedInstance

    # evaluate consistency assertions

    # evaluate variable sets not in consistency assertions
    for instanceQname in orderedInstancesList:
        for modelVariableSet in instanceProducingVariableSets[instanceQname]:
            # produce variable evaluations
            from arelle.FormulaEvaluator import evaluate
            try:
                evaluate(xpathContext, modelVariableSet)
            except XPathContext.XPathException as err:
                val.modelXbrl.error(
                    _("Variable set \n{0} \nException: \n{1}").format(
                        modelVariableSet, err.message), "err", err.code)

    # log assertion result counts
    asserTests = {}
    for exisValAsser in val.modelXbrl.modelVariableSets:
        if isinstance(exisValAsser, ModelVariableSetAssertion):
            asserTests[exisValAsser.id] = (exisValAsser.countSatisfied,
                                           exisValAsser.countNotSatisfied)
            if formulaOptions.traceAssertionResultCounts:
                val.modelXbrl.error(
                    _("{0} Assertion {1} evaluations : {2} satisfied, {3} not satisfied"
                      ).format(
                          "Existence"
                          if isinstance(exisValAsser,
                                        ModelExistenceAssertion) else "Value",
                          exisValAsser.id, exisValAsser.countSatisfied,
                          exisValAsser.countNotSatisfied), "info",
                    "formula:trace")

    for modelRel in val.modelXbrl.relationshipSet(
            XbrlConst.consistencyAssertionFormula).modelRelationships:
        if modelRel.fromModelObject and modelRel.toModelObject and isinstance(
                modelRel.toModelObject, ModelFormula):
            consisAsser = modelRel.fromModelObject
            asserTests[consisAsser.id] = (consisAsser.countSatisfied,
                                          consisAsser.countNotSatisfied)
            if formulaOptions.traceAssertionResultCounts:
                val.modelXbrl.error(
                    _("Consistency Assertion {0} evaluations : {1} satisfied, {2} not satisfied"
                      ).format(consisAsser.id, consisAsser.countSatisfied,
                               consisAsser.countNotSatisfied), "info",
                    "formula:trace")

    if asserTests:
        val.modelXbrl.error(
            _("Assertion results {0}").format(asserTests), "asrtNoLog",
            asserTests)

    # display output instance
    if outputXbrlInstance:
        if val.modelXbrl.formulaOutputInstance:
            # close prior instance, usually closed by caller to validate as it may affect UI on different thread
            val.modelXbrl.formulaOutputInstance.close()
        val.modelXbrl.formulaOutputInstance = outputXbrlInstance
Example #14
0
def validate(val):
    formulaOptions = val.modelXbrl.modelManager.formulaOptions
    XPathParser.initializeParser(val)
    val.modelXbrl.modelManager.showStatus(_("Compiling formulae"))
    initialErrorCount = val.modelXbrl.logCountErr
    
    # global parameter names
    parameterQnames = set()
    instanceQnames = set()
    parameterDependencies = {}
    instanceDependencies = defaultdict(set)  # None-key entries are non-formula dependencies
    dependencyResolvedParameters = set()
    orderedParameters = []
    orderedInstances = []
    for paramQname, modelParameter in val.modelXbrl.qnameParameters.items():
        if isinstance(modelParameter, ModelParameter):
            modelParameter.compile()
            parameterDependencies[paramQname] = modelParameter.variableRefs()
            parameterQnames.add(paramQname)
            if isinstance(modelParameter, ModelInstance):
                instanceQnames.add(paramQname)
            # duplicates checked on loading modelDocument
            
    #resolve dependencies
    resolvedAParameter = True
    while (resolvedAParameter):
        resolvedAParameter = False
        for paramQname in parameterQnames:
            if paramQname not in dependencyResolvedParameters and \
               len(parameterDependencies[paramQname] - dependencyResolvedParameters) == 0:
                dependencyResolvedParameters.add(paramQname)
                orderedParameters.append(paramQname)
                resolvedAParameter = True
    # anything unresolved?
    for paramQname in parameterQnames:
        if paramQname not in dependencyResolvedParameters:
            circularOrUndefDependencies = parameterDependencies[paramQname] - dependencyResolvedParameters
            undefinedVars = circularOrUndefDependencies - parameterQnames 
            paramsCircularDep = circularOrUndefDependencies - undefinedVars
            if len(undefinedVars) > 0:
                val.modelXbrl.error(
                    _("Undefined dependencies in parameter {0}, to names {1}").format(
                          paramQname, ", ".join((str(v) for v in undefinedVars))), 
                    "err", "xbrlve:unresolvedDependency")
            if len(paramsCircularDep) > 0:
                val.modelXbrl.error(
                    _("Cyclic dependencies in parameter {0}, to names {1}").format(
                          paramQname, ", ".join((str(d) for d in paramsCircularDep)) ), 
                    "err", "xbrlve:parameterCyclicDependencies")
            
    for custFnSig in val.modelXbrl.modelCustomFunctionSignatures.values():
        custFnQname = custFnSig.qname
        if custFnQname.namespaceURI == "XbrlConst.xfi":
            val.modelXbrl.error(
                _("Custom function {0} has namespace reserved for functions in the function registry {1}").format(
                      str(custFnQname), custFnQname.namespaceURI ), 
                "err", "xbrlve:noProhibitedNamespaceForCustomFunction")
        # any custom function implementations?
        for modelRel in val.modelXbrl.relationshipSet(XbrlConst.functionImplementation).fromModelObject(custFnSig):
            custFnImpl = modelRel.toModelObject
            custFnSig.customFunctionImplementation = custFnImpl
            if len(custFnImpl.inputNames) != len(custFnSig.inputTypes):
                val.modelXbrl.error(
                    _("Custom function {0} signature has {1} parameters but implementation has {2}, must be matching").format(
                          str(custFnQname), len(custFnSig.inputTypes), len(custFnImpl.inputNames) ), 
                    "err", "xbrlcfie:inputMismatch")
        
    for custFnImpl in val.modelXbrl.modelCustomFunctionImplementations:
        if not val.modelXbrl.relationshipSet(XbrlConst.functionImplementation).toModelObject(custFnImpl):
            val.modelXbrl.error(
                _("Custom function implementation {0} has no relationship from any custom function signature").format(
                      custFnImpl.xlinkLabel), 
                "err", "xbrlcfie:missingCFIRelationship")
        custFnImpl.compile()
            
    # xpathContext is needed for filter setup for expressions such as aspect cover filter
    # determine parameter values
    xpathContext = XPathContext.create(val.modelXbrl)
    for paramQname in orderedParameters:
        if not isinstance(modelParameter, ModelInstance):
            modelParameter = val.modelXbrl.qnameParameters[paramQname]
            asType = modelParameter.asType
            asLocalName = asType.localName if asType else "string"
            try:
                if val.parameters and paramQname in val.parameters:
                    paramDataType, paramValue = val.parameters[paramQname]
                    typeLocalName = paramDataType.localName if paramDataType else "string"
                    value = FunctionXs.call(xpathContext, None, typeLocalName, [paramValue])
                    result = FunctionXs.call(xpathContext, None, asLocalName, [value])
                    if formulaOptions.traceParameterInputValue:
                        val.modelXbrl.error( _("Parameter {0} input {1}").format( paramQname, result),
                            "info", "formula:trace")
                else:
                    result = modelParameter.evaluate(xpathContext, asType)
                    if formulaOptions.traceParameterExpressionResult:
                        val.modelXbrl.error( _("Parameter {0} result {1}").format( paramQname, result),
                            "info", "formula:trace")
                xpathContext.inScopeVars[paramQname] = result    # make visible to subsequent parameter expression 
            except XPathContext.XPathException as err:
                val.modelXbrl.error( _("Parameter \n{0} \nException: \n{1}").format( paramQname, err.message),
                    "err", "xbrlve:parameterTypeMismatch" if err.code == "err:FORG0001" else err.code)

    produceOutputXbrlInstance = False
    instanceProducingVariableSets = defaultdict(list)
        
    for modelVariableSet in val.modelXbrl.modelVariableSets:
        varSetInstanceDependencies = set()
        if isinstance(modelVariableSet, ModelFormula):
            instanceQname = None
            for modelRel in val.modelXbrl.relationshipSet(XbrlConst.formulaInstance).fromModelObject(modelVariableSet):
                instance = modelRel.toModelObject
                if isinstance(instance, ModelInstance):
                    if instanceQname is None:
                        instanceQname = instance.qname
                    else:
                        val.modelXbrl.error(
                            _("Multiple output instances for formula {0}, to names {1}, {2}").format(
                                  modelVariableSet.xlinkLabel, instanceQname, instance.qname ), 
                            "info", "arelle:multipleOutputInstances")
            if instanceQname is None: 
                instanceQname = XbrlConst.qnStandardOutputInstance
                instanceQnames.add(instanceQname)
            modelVariableSet.outputInstanceQname = instanceQname
            if val.validateSBRNL:
                val.modelXbrl.error(
                    _("Formula linkbase {0} formula:formula {1} is not allowed").format(
                        os.path.basename(modelVariableSet.modelDocument.uri), modelVariableSet.xlinkLabel), 
                    "err", "SBR.NL.2.3.9.03")
        else:
            instanceQname = None
            modelVariableSet.countSatisfied = 0
            modelVariableSet.countNotSatisfied = 0
            checkValidationMessages(val, modelVariableSet)
        instanceProducingVariableSets[instanceQname].append(modelVariableSet)
        modelVariableSet.outputInstanceQname = instanceQname
        if modelVariableSet.aspectModel not in ("non-dimensional", "dimensional"):
            val.modelXbrl.error(
                _("Variable set {0}, aspect model {1} not recognized").format(
                      modelVariableSet.xlinkLabel, modelVariableSet.aspectModel), 
                "err", "xbrlve:unknownAspectModel")
        modelVariableSet.compile()
        modelVariableSet.hasConsistencyAssertion = False
            
        #determine dependencies within variable sets
        nameVariables = {}
        qnameRels = {}
        definedNamesSet = set()
        for modelRel in val.modelXbrl.relationshipSet(XbrlConst.variableSet).fromModelObject(modelVariableSet):
            varqname = modelRel.variableQname
            if varqname:
                qnameRels[varqname] = modelRel
                toVariable = modelRel.toModelObject
                if varqname not in definedNamesSet:
                    definedNamesSet.add(varqname)
                if varqname not in nameVariables:
                    nameVariables[varqname] = toVariable
                elif nameVariables[varqname] != toVariable:
                    val.modelXbrl.error(
                        _("Multiple variables named {1} in variable set {0}").format(
                              modelVariableSet.xlinkLabel, varqname ), 
                        "err", "xbrlve:duplicateVariableNames")
                fromInstanceQnames = None
                for instRel in val.modelXbrl.relationshipSet(XbrlConst.instanceVariable).toModelObject(toVariable):
                    fromInstance = instRel.fromModelObject
                    if isinstance(fromInstance, ModelInstance):
                        fromInstanceQname = fromInstance.qname
                        varSetInstanceDependencies.add(fromInstanceQname)
                        instanceDependencies[instanceQname].add(fromInstanceQname)
                        if fromInstanceQnames is None: fromInstanceQnames = set()
                        fromInstanceQnames.add(fromInstanceQname)
                if fromInstanceQnames is None:
                    varSetInstanceDependencies.add(XbrlConst.qnStandardInputInstance)
                    if instanceQname: instanceDependencies[instanceQname].add(XbrlConst.qnStandardInputInstance)
                toVariable.fromInstanceQnames = fromInstanceQnames
            else:
                val.modelXbrl.error(
                    _("Variables name {1} cannot be determined on arc from {0}").format(
                          modelVariableSet.xlinkLabel, modelRel.variablename ), 
                    "err", "xbrlve:variableNameResolutionFailure")
        definedNamesSet |= parameterQnames
                
        variableDependencies = {}
        for modelRel in val.modelXbrl.relationshipSet(XbrlConst.variableSet).fromModelObject(modelVariableSet):
            variable = modelRel.toModelObject
            if isinstance(variable, (ModelParameter,ModelVariable)):    # ignore anything not parameter or variable
                varqname = modelRel.variableQname
                depVars = variable.variableRefs()
                variableDependencies[varqname] = depVars
                if len(depVars) > 0 and formulaOptions.traceVariablesDependencies:
                    val.modelXbrl.error(_("Variable set {0}, variable {1}, dependences {2}").format(
                                  modelVariableSet.xlinkLabel, varqname, depVars), 
                                  "info", "formula:trace") 
                definedNamesSet.add(varqname)
                # check for fallback value variable references
                if isinstance(variable, ModelFactVariable):
                    for depVar in XPathParser.variableReferencesSet(variable.fallbackValueProg, variable.element):
                        if depVar in qnameRels and isinstance(qnameRels[depVar].toModelObject,ModelVariable):
                            val.modelXbrl.error(_("Variable set {0} fallbackValue '{1}' cannot refer to variable {2}").format(
                                          modelVariableSet.xlinkLabel, variable.fallbackValue, depVar),
                                          "err", "xbrlve:factVariableReferenceNotAllowed") 
                    # check for covering aspect not in variable set aspect model
                    checkFilterAspectModel(val, modelVariableSet, variable.filterRelationships, xpathContext)

        orderedNameSet = set()
        orderedNameList = []
        orderedAVariable = True
        while (orderedAVariable):
            orderedAVariable = False
            for varqname, depVars in variableDependencies.items():
                if varqname not in orderedNameSet and len(depVars - parameterQnames - orderedNameSet) == 0:
                    orderedNameList.append(varqname)
                    orderedNameSet.add(varqname)
                    orderedAVariable = True
                if varqname in instanceQnames:
                    varSetInstanceDependencies.add(varqname)
                    instanceDependencies[instanceQname].add(varqname)
                elif isinstance(nameVariables.get(varqname), ModelInstance):
                    instqname = nameVariables[varqname].qname
                    varSetInstanceDependencies.add(instqname)
                    instanceDependencies[instanceQname].add(instqname)
                    
        # anything unresolved?
        for varqname, depVars in variableDependencies.items():
            if varqname not in orderedNameSet:
                circularOrUndefVars = depVars - parameterQnames - orderedNameSet
                undefinedVars = circularOrUndefVars - definedNamesSet 
                varsCircularDep = circularOrUndefVars - undefinedVars
                if len(undefinedVars) > 0:
                    val.modelXbrl.error(
                        _("Undefined variable dependencies in variable st {0}, from variable {1} to {2}").format(
                              modelVariableSet.xlinkLabel, varqname, undefinedVars), 
                        "err", "xbrlve:unresolvedDependency")
                if len(varsCircularDep) > 0:
                    val.modelXbrl.error(
                        _("Cyclic dependencies in variable set {0}, from variable {1} to {2}").format(
                              modelVariableSet.xlinkLabel, varqname, varsCircularDep ), 
                        "err", "xbrlve:cyclicDependencies")
                    
        # check unresolved variable set dependencies
        for varSetDepVarQname in modelVariableSet.variableRefs():
            if varSetDepVarQname not in orderedNameSet and varSetDepVarQname not in parameterQnames:
                val.modelXbrl.error(
                    _("Undefined variable dependency in variable set {0}, {1}").format(
                          modelVariableSet.xlinkLabel, varSetDepVarQname), 
                    "err", "xbrlve:unresolvedDependency")
            if varSetDepVarQname in instanceQnames:
                varSetInstanceDependencies.add(varSetDepVarQname)
                instanceDependencies[instanceQname].add(varSetDepVarQname)
            elif isinstance(nameVariables.get(varSetDepVarQname), ModelInstance):
                instqname = nameVariables[varSetDepVarQname].qname
                varSetInstanceDependencies.add(instqname)
                instanceDependencies[instanceQname].add(instqname)
        
        if formulaOptions.traceVariablesOrder:
            val.modelXbrl.error(_("Variable set {0}, variables order: {1}").format(
                          modelVariableSet.xlinkLabel, orderedNameList), "info", "formula:trace") 
        
        if (formulaOptions.traceVariablesDependencies and len(varSetInstanceDependencies) > 0 and
            varSetInstanceDependencies != {XbrlConst.qnStandardInputInstance}):
            val.modelXbrl.error(_("Variable set {0}, instance dependences {1}").format(
                          modelVariableSet.xlinkLabel, varSetInstanceDependencies), 
                          "info", "formula:trace") 
            
        modelVariableSet.orderedVariableRelationships = []
        for varqname in orderedNameList:
            if varqname in qnameRels:
                modelVariableSet.orderedVariableRelationships.append(qnameRels[varqname])
                
        # check existence assertion variable dependencies
        if isinstance(modelVariableSet, ModelExistenceAssertion):
            for depVar in modelVariableSet.variableRefs():
                if depVar in qnameRels and isinstance(qnameRels[depVar].toModelObject,ModelVariable):
                    val.modelXbrl.error(_("Existence Assertion {0}, cannot refer to variable {1}").format(
                                  modelVariableSet.xlinkLabel, depVar),
                                  "err", "xbrleae:variableReferenceNotAllowed") 
                    
        # check messages variable dependencies
        checkValidationMessageVariables(val, modelVariableSet, qnameRels)
                        
        # check preconditions
        modelVariableSet.preconditions = []
        for modelRel in val.modelXbrl.relationshipSet(XbrlConst.variableSetPrecondition).fromModelObject(modelVariableSet):
            precondition = modelRel.toModelObject
            if isinstance(precondition, ModelPrecondition):
                modelVariableSet.preconditions.append(precondition)
                
        # check for variable sets referencing fact or general variables
        for modelRel in val.modelXbrl.relationshipSet(XbrlConst.variableSetFilter).fromModelObject(modelVariableSet):
            varSetFilter = modelRel.toModelObject
            if modelRel.isCovered:
                val.modelXbrl.error(_("Variable set {0}, filter {1}, cannot be covered").format(
                              modelVariableSet.xlinkLabel, varSetFilter.xlinkLabel),
                              "wrn", "arelle:variableSetFilterCovered") 
                modelRel._isCovered = False # block group filter from being able to covere
            for depVar in varSetFilter.variableRefs():
                if depVar in qnameRels and isinstance(qnameRels[depVar].toModelObject,ModelVariable):
                    val.modelXbrl.error(_("Variable set {0}, filter {1}, cannot refer to variable {2}").format(
                                  modelVariableSet.xlinkLabel, varSetFilter.xlinkLabel, depVar),
                                  "err", "xbrlve:factVariableReferenceNotAllowed") 
                    
        # check aspects of formula
        if isinstance(modelVariableSet, ModelFormula):
            checkFormulaRules(val, modelVariableSet, nameVariables)
            
    # determine instance dependency order
    orderedInstancesSet = set()
    stdInpInst = {XbrlConst.qnStandardInputInstance}
    orderedInstancesList = []
    orderedAnInstance = True
    while (orderedAnInstance):
        orderedAnInstance = False
        for instqname, depInsts in instanceDependencies.items():
            if instqname and instqname not in orderedInstancesSet and len(depInsts - stdInpInst - orderedInstancesSet) == 0:
                orderedInstancesList.append(instqname)
                orderedInstancesSet.add(instqname)
                orderedAnInstance = True
    orderedInstancesList.append(None)  # assertions come after all formulas that produce outputs

    # anything unresolved?
    for instqname, depInsts in instanceDependencies.items():
        if instqname not in orderedInstancesSet:
            # can also be satisfied from an input DTS
            missingDependentInstances = depInsts - stdInpInst
            if val.parameters: missingDependentInstances -= val.parameters.keys() 
            if instqname:
                if missingDependentInstances:
                    val.modelXbrl.error(
                        _("Cyclic dependencies of instance {0} produced by a formula, with variables consuming instances {1}").format(
                              instqname, missingDependentInstances ), 
                        "err", "xbrlvarinste:instanceVariableRecursionCycle")
                elif instqname == XbrlConst.qnStandardOutputInstance:
                    orderedInstancesSet.add(instqname)
                    orderedInstancesList.append(instqname) # standard output formula, all input dependencies in parameters
            ''' future check?  if instance has no external input or producing formula
            else:
                val.modelXbrl.error(
                    _("Unresolved dependencies of an assertion's variables on instances {0}").format(
                          depInsts - stdInpInst ), 
                    "err", "xbrlvarinste:instanceVariableRecursionCycle")
            '''

    if formulaOptions.traceVariablesOrder and len(orderedInstancesList) > 1:
        val.modelXbrl.error(_("Variable instances processing order: {0}").format(
                            orderedInstancesList), "info", "formula:trace") 

    # linked consistency assertions
    for modelRel in val.modelXbrl.relationshipSet(XbrlConst.consistencyAssertionFormula).modelRelationships:
        if modelRel.fromModelObject and modelRel.toModelObject and isinstance(modelRel.toModelObject,ModelFormula):
            consisAsser = modelRel.fromModelObject
            consisAsser.countSatisfied = 0
            consisAsser.countNotSatisfied = 0
            if consisAsser.hasProportionalAcceptanceRadius and consisAsser.hasAbsoluteAcceptanceRadius:
                val.modelXbrl.error( _("Consistency assertion {0} has both absolute and proportional acceptance radii").format( 
                     consisAsser.xlinkLabel),
                    "err", "xbrlcae:acceptanceRadiusConflict")
            consisAsser.orderedVariableRelationships = []
            for consisParamRel in val.modelXbrl.relationshipSet(XbrlConst.consistencyAssertionParameter).fromModelObject(consisAsser):
                if isinstance(consisParamRel.toModelObject, ModelVariable):
                    val.modelXbrl.error( _("Consistency assertion {0} has relationship to a {1} {2}").format( 
                         consisAsser.xlinkLabel, consisParamRel.toModelObject.element.localName, consisParamRel.toModelObject.xlinkLabel),
                        "err", "xbrlcae:variablesNotAllowed")
                else:
                    consisAsser.orderedVariableRelationships.append(consisParamRel)
            consisAsser.compile()
            modelRel.toModelObject.hasConsistencyAssertion = True

    if initialErrorCount < val.modelXbrl.logCountErr:
        return  # don't try to execute
        

    # formula output instances    
    if instanceQnames:      
        schemaRefs = [val.modelXbrl.modelDocument.relativeUri(referencedDoc.uri)
                        for referencedDoc in val.modelXbrl.modelDocument.referencesDocument.keys()
                            if referencedDoc.type == ModelDocument.Type.SCHEMA]
        
    outputXbrlInstance = None
    for instanceQname in instanceQnames:
        if instanceQname == XbrlConst.qnStandardInputInstance:
            continue    # always present the standard way
        if val.parameters and instanceQname in val.parameters:
            namedInstance = val.parameters[instanceQname][1]
        else:   # empty intermediate instance 
            uri = val.modelXbrl.modelDocument.filepath[:-4] + "-output-XBRL-instance"
            if instanceQname != XbrlConst.qnStandardOutputInstance:
                uri = uri + "-" + instanceQname.localName
            uri = uri + ".xml"
            namedInstance = ModelXbrl.create(val.modelXbrl.modelManager, 
                                             newDocumentType=ModelDocument.Type.INSTANCE,
                                             url=uri,
                                             schemaRefs=schemaRefs,
                                             isEntry=True)
        xpathContext.inScopeVars[instanceQname] = namedInstance
        if instanceQname == XbrlConst.qnStandardOutputInstance:
            outputXbrlInstance = namedInstance
        
    # evaluate consistency assertions
    
    # evaluate variable sets not in consistency assertions
    for instanceQname in orderedInstancesList:
        for modelVariableSet in instanceProducingVariableSets[instanceQname]:
            # produce variable evaluations
            from arelle.FormulaEvaluator import evaluate
            try:
                evaluate(xpathContext, modelVariableSet)
            except XPathContext.XPathException as err:
                val.modelXbrl.error( _("Variable set \n{0} \nException: \n{1}").format( modelVariableSet, err.message),
                    "err", err.code)
            
    # log assertion result counts
    asserTests = {}
    for exisValAsser in val.modelXbrl.modelVariableSets:
        if isinstance(exisValAsser, ModelVariableSetAssertion):
            asserTests[exisValAsser.id] = (exisValAsser.countSatisfied, exisValAsser.countNotSatisfied)
            if formulaOptions.traceAssertionResultCounts:
                val.modelXbrl.error( _("{0} Assertion {1} evaluations : {2} satisfied, {3} not satisfied").format(
                    "Existence" if isinstance(exisValAsser, ModelExistenceAssertion) else "Value", 
                    exisValAsser.id, exisValAsser.countSatisfied, exisValAsser.countNotSatisfied),
                    "info", "formula:trace")

    for modelRel in val.modelXbrl.relationshipSet(XbrlConst.consistencyAssertionFormula).modelRelationships:
        if modelRel.fromModelObject and modelRel.toModelObject and isinstance(modelRel.toModelObject,ModelFormula):
            consisAsser = modelRel.fromModelObject
            asserTests[consisAsser.id] = (consisAsser.countSatisfied, consisAsser.countNotSatisfied)
            if formulaOptions.traceAssertionResultCounts:
                val.modelXbrl.error( _("Consistency Assertion {0} evaluations : {1} satisfied, {2} not satisfied").format(
                    consisAsser.id, consisAsser.countSatisfied, consisAsser.countNotSatisfied),
                    "info", "formula:trace")
            
    if asserTests:
        val.modelXbrl.error( _("Assertion results {0}").format(asserTests),
            "asrtNoLog", asserTests)

    # display output instance
    if outputXbrlInstance:
        if val.modelXbrl.formulaOutputInstance:
            # close prior instance, usually closed by caller to validate as it may affect UI on different thread
            val.modelXbrl.formulaOutputInstance.close()
        val.modelXbrl.formulaOutputInstance = outputXbrlInstance