def one_or_more(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() if len(args[0]) < 1: raise XPathContext.FunctionNumArgs( errCode='err:FORG0004', errText=_( 'fn:one-or-more called with a sequence containing no items')) return args[0]
def exactly_one(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() if len(args[0]) != 1: raise XPathContext.FunctionNumArgs( errCode='err:FORG0005', errText= _('fn:exactly-one called with a sequence containing zero or more than one item' )) return args[0]
def zero_or_one(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() if len(args[0]) > 1: raise XPathContext.FunctionNumArgs( errCode='err:FORG0003', errText= _('fn:zero-or-one called with a sequence containing more than one item' )) return args[0]
def subsequence(xc, p, contextItem, args): if len(args) not in (2,3): raise XPathContext.FunctionNumArgs() l = len(args) if l < 2 or l > 3: raise XPathContext.FunctionNumArgs() sequence = args[0] start = _INT(round( numericArg(xc, p, args, 1) )) - 1 if l == 3: length = _INT(round( numericArg(xc, p, args, 2) )) if start < 0: length += start if length < 0: length = 0 start = 0 return sequence[start:start + length] if start < 0: start = 0 return sequence[start:]
def linkbase_link_roles(xc, p, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() arcroleURI = stringArg(xc, args, 0, "xs:string") relationshipSet = xc.modelXbrl.relationshipSet(arcroleURI) if relationshipSet: return [anyURI(linkrole) for linkrole in relationshipSet.linkRoleUris] return ()
def my_fn_PDxEV(xc, p, contextItem, args): if len(args) != 2: raise XPathContext.FunctionNumArgs() PDseq = args[0] if isinstance(args[0], (list, tuple)) else (args[0], ) EVseq = args[1] if isinstance(args[1], (list, tuple)) else (args[1], ) dimQname = qname("{http://www.example.com/wgt-avg}ExposuresDimension") PDxEV = [] for pd in PDseq: if pd.context is not None: pdDim = pd.context.dimValue(dimQname) for ev in EVseq: if ev.context is not None: evDim = ev.context.dimValue(dimQname) if pdDim is not None and isinstance( pdDim, ModelDimensionValue): dimEqual = pdDim.isEqualTo(evDim, equalMode=XbrlUtil.S_EQUAL2) elif evDim is not None and isinstance( evDim, ModelDimensionValue): dimEqual = evDim.isEqualTo(pdDim, equalMode=XbrlUtil.S_EQUAL2) else: dimEqual = (pdDim == evDim) if dimEqual: PDxEV.append(pd.xValue * ev.xValue) break return PDxEV
def fn_sum(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() addends = xc.atomize(p, args[0]) try: if len(addends) == 0: return 0 # xpath allows empty sequence argument hasFloat = False hasDecimal = False for a in addends: if math.isnan(a): return NaN if isinstance(a, float): hasFloat = True elif isinstance(a, Decimal): hasDecimal = True if hasFloat and hasDecimal: # promote decimals to float addends = [ float(a) if isinstance(a, Decimal) else a for a in addends ] return sum(addends) except TypeError: raise XPathContext.FunctionArgType(1, "summable sequence", addends, errCode='err:FORG0001')
def concept_data_type(xc, p, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() typeQname = concept(xc, p, args).typeQname if not typeQname or typeQname.localName.endswith( ModelObject.anonymousTypeSuffix): return () return typeQname
def seconds_from_duration(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() d = anytypeArg(xc, args, 0, 'duration', missingArgFallback=()) if d == (): return d if isinstance(d, DayTimeDuration): return 0 if isinstance(d, YearMonthDuration): return d.dayHrsMinsSecs[2] raise XPathContext.FunctionArgType(1,"xs:duration")
def fn_dateTime(xc, p, contextItem, args): if len(args) != 2: raise XPathContext.FunctionNumArgs() date = anytypeArg(xc, args, 0, "xs:date", missingArgFallback=()) time = anytypeArg(xc, args, 1, "xs:time", missingArgFallback=()) if date is None or time is None: return () return dateTime(date) + dayTimeDuration(time)
def my_fn_PDxEV(xc, p, contextItem, args): if len(args) != 2: raise XPathContext.FunctionNumArgs() PDseq = flattenSequence(args[0]) EVseq = flattenSequence(args[1]) dimQname = qname("{http://www.example.com/wgt-avg}ExposuresDimension") PDxEV = [] for pd in PDseq: if pd.context is not None: pdDim = pd.context.dimValue(dimQname) for ev in EVseq: if ev.context is not None: evDim = ev.context.dimValue(dimQname) if pdDim is not None and isinstance(pdDim,ModelDimensionValue): dimEqual = pdDim.isEqualTo(evDim, equalMode=XbrlUtil.S_EQUAL2) elif evDim is not None and isinstance(evDim,ModelDimensionValue): dimEqual = evDim.isEqualTo(pdDim, equalMode=XbrlUtil.S_EQUAL2) else: dimEqual = (pdDim == evDim) if dimEqual: pdX = pd.xValue evX = ev.xValue # type promotion required if isinstance(pdX,Decimal) and isinstance(evX,float): pdX = float(pdX) elif isinstance(evX,Decimal) and isinstance(pdX,float): pdX = float(evX) PDxEV.append(pdX * evX) break return PDxEV
def doc(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() uri = stringArg(xc, args, 0, "xs:string", emptyFallback=None) if uri is None: return () if xc.progHeader is None or xc.progHeader.element is None: raise XPathContext.XPathException( p, 'err:FODC0005', _('Function xf:doc no formula resource element for {0}').format( uri)) if not UrlUtil.isValid(uri): raise XPathContext.XPathException( p, 'err:FODC0005', _('Function xf:doc $uri is not valid {0}').format(uri)) normalizedUri = xc.modelXbrl.modelManager.cntlr.webCache.normalizeUrl( uri, xc.progHeader.element.modelDocument.baseForElement( xc.progHeader.element)) if normalizedUri in xc.modelXbrl.urlDocs: return xc.modelXbrl.urlDocs[normalizedUri].xmlDocument modelDocument = ModelDocument.load(xc.modelXbrl, normalizedUri) if modelDocument is None: raise XPathContext.XPathException( p, 'err:FODC0005', _('Function xf:doc $uri not successfully loaded {0}').format(uri)) # assure that document is validated XmlValidate.validate(xc.modelXbrl, modelDocument.xmlRootElement) return modelDocument.xmlDocument
def xfxc_element(xc, p, contextItem, args): if not 2 <= len(args) <= 4: raise XPathContext.FunctionNumArgs() qn = qnameArg(xc, p, args, 0, 'QName', emptyFallback=None) attrArg = args[1] if isinstance(args[1], (list, tuple)) else (args[1], ) # attributes have to be pairs if attrArg: if len(attrArg) & 1 or any( not isinstance(attrArg[i], (QName, _STR_BASE)) for i in range(0, len(attrArg), 2)): raise XPathContext.FunctionArgType( 1, "((xs:qname|xs:string),xs:anyAtomicValue)", errCode="xfxce:AttributesNotNameValuePairs") else: attrParam = [ (attrArg[i], attrArg[i + 1] ) # need name-value pairs for XmlUtil function for i in range(0, len(attrArg), 2) ] else: attrParam = None value = atomicArg(xc, p, args, 2, "xs:anyAtomicType", emptyFallback='') if not value: # be sure '' is None so no text node is created value = None if len(args) < 4: childElements = None else: childElements = xc.flattenSequence(args[3]) # scratchpad instance document emulates fn:doc( ) to hold XML nodes scratchpadXmlDocUrl = "http://www.xbrl.org/2012/function/creation/xml_scratchpad.xml" if scratchpadXmlDocUrl in xc.modelXbrl.urlDocs: modelDocument = xc.modelXbrl.urlDocs[scratchpadXmlDocUrl] else: # create scratchpad xml document # this will get the fake instance document in the list of modelXbrl docs so that it is garbage collected from arelle import ModelDocument modelDocument = ModelDocument.create( xc.modelXbrl, ModelDocument.Type.UnknownXML, scratchpadXmlDocUrl, initialXml= "<xfc:dummy xmlns:xfc='http://www.xbrl.org/2012/function/creation'/>" ) newElement = XmlUtil.addChild(modelDocument.xmlRootElement, qn, attributes=attrParam, text=value) if childElements: for element in childElements: if isinstance(element, etree.ElementBase): newElement.append(element) # node myst be validated for use in instance creation (typed dimension references) XmlValidate.validate(xc.modelXbrl, newElement) return newElement
def element_name(xc, p, args, elementParent=False): if len(args) != 1: raise XPathContext.FunctionNumArgs() modelRel = anytypeArg(xc, args, 0, "arelle:ModelRelationship", None) if not modelRel: raise XPathContext.FunctionArgType(1, "arelle:modelRelationship") element = modelRel.element if elementParent: element = element.parentNode return qname(element)
def measure_name(xc, p, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() if len(args[0]) != 1: raise XPathContext.FunctionArgType(1, "xbrl:measure") unit = args[0][0] if isinstance(unit,xml.dom.Node) and unit.nodeType == 1 and \ unit.localName == "measure" and unit.namespaceURI == XbrlConst.xbrli: return qname(unit, XmlUtil.text(unit)) raise XPathContext.FunctionArgType(1, "xbrl:unit")
def fn_round(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() x = numericArg(xc, p, args) if math.isinf(x) or math.isnan(x): return x return _INT( x + (DECIMAL_5 if isinstance(x, Decimal) else .5)) # round towards +inf
def call(xc, p, localname, args): try: if localname not in ixtFunctions: raise ixtFunctionNotAvailable if len(args) != 1: raise XPathContext.FunctionNumArgs() if len(args[0]) != 1: raise XPathContext.FunctionArgType(1,"xs:string") return ixtFunctions[localname](str(args[0][0])) except ixtFunctionNotAvailable: raise XPathContext.FunctionNotAvailable("xfi:{0}".format(localname))
def xfm_tan(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() x = numericArg(xc, p, args, 0, emptyFallback=()) if x != (): if math.isinf(x): return NaN return math.tan(x) return ()
def is_period_type(args, periodElement): if len(args) != 1: raise XPathContext.FunctionNumArgs() if len(args[0]) != 1: raise XPathContext.FunctionArgType(1, "xbrl:period") period = args[0][0] if isinstance(period,xml.dom.Node) and period.nodeType == 1 and \ period.localName == "period" and period.namespaceURI == XbrlConst.xbrli: return XmlUtil.hasChild(period, XbrlConst.xbrli, periodElement) raise XPathContext.FunctionArgType(1, "xbrl:period")
def call(xc, p, qn, args): try: _ixtFunction = ixtNamespaceFunctions[qn.namespaceURI][qn.localName] except KeyError: raise XPathContext.FunctionNotAvailable(str(qn)) if len(args) != 1: raise XPathContext.FunctionNumArgs() if len(args[0]) != 1: raise XPathContext.FunctionArgType(1, "xs:string") return _ixtFunction(str(args[0][0]))
def format_number(xc, p, args): if len(args) != 2: raise XPathContext.FunctionNumArgs() value = numericArg(xc, p, args, 0, missingArgFallback='NaN', emptyFallback='NaN') picture = stringArg(xc, args, 1, "xs:string", missingArgFallback='', emptyFallback='') try: return format_picture(xc.modelXbrl.locale, value, picture) except ValueError as err: raise XPathContext.XPathException(p, 'err:FODF1310', str(err) )
def arcrole_definition(xc, p, args): if len(args) == 2: raise XPathContext.FunctionNumArgs() arcroleURI = stringArg(xc, args, 0, "xs:string", emptyFallback='') modelArcroleTypes = xc.modelXbrl.arcroleTypes.get(arcroleURI) if modelArcroleTypes is not None and len(modelArcroleTypes) > 0: arcroledefinition = modelArcroleTypes[0].definition if arcroledefinition is not None: return arcroledefinition return ()
def fn_abs(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() x = numericArg(xc, p, args) if math.isinf(x): x = float('inf') elif not math.isnan(x): x = abs(x) return x
def matches(xc, p, contextItem, args): if not 2 <= len(args) <= 3: raise XPathContext.FunctionNumArgs() input = stringArg(xc, args, 0, "xs:string?", emptyFallback=()) pattern = stringArg(xc, args, 1, "xs:string", emptyFallback=()) try: return bool(re.search(pattern,input,flags=regexFlags(xc, p, args, 2))) except sre_constants.error as err: raise XPathContext.XPathException(p, 'err:FORX0002', _('fn:matches regular expression pattern error: {0}').format(err))
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)
def string(xc, p, contextItem, args): if len(args) > 1: raise XPathContext.FunctionNumArgs() x = stringArg(xc, args, 0, "item()?", missingArgFallback=contextItem, emptyFallback='') return str(x)
def fn_round_half_to_even(xc, p, contextItem, args): if len(args) > 2 or len(args) == 0: raise XPathContext.FunctionNumArgs() x = numericArg(xc, p, args) if len(args) == 2: precision = args[1] if len(precision) != 1 or not isinstance(precision[0],_INT_TYPES): raise XPathContext.FunctionArgType(2,"integer") precision = precision[0] return round(x, precision) return round(x)
def xfm_atan(xc, p, contextItem, args): if len(args) != 1: raise XPathContext.FunctionNumArgs() x = numericArg(xc, p, args, 0, emptyFallback=()) if x != (): try: return math.atan(x) except ValueError: return NaN return ()
def replace(xc, p, contextItem, args): if not 3 <= len(args) <= 4: raise XPathContext.FunctionNumArgs() input = stringArg(xc, args, 0, "xs:string?", emptyFallback=()) pattern = stringArg(xc, args, 1, "xs:string", emptyFallback=()) replacement = stringArg(xc, args, 2, "xs:string", emptyFallback=()) return re.sub(pattern, replacement, input, flags=regexFlags(xc, p, args, 3))
def compare(xc, p, contextItem, args): if len(args) == 3: raise fnFunctionNotAvailable() if len(args) != 2: raise XPathContext.FunctionNumArgs() comparand1 = stringArg(xc, args, 0, "xs:string?", emptyFallback=()) comparand2 = stringArg(xc, args, 1, "xs:string?", emptyFallback=()) if comparand1 == () or comparand2 == (): return () if comparand1 == comparand2: return 0 if comparand1 < comparand2: return -1 return 1