Example #1
0
def addChild(parent, childName1, childName2=None, attributes=None, text=None, afterSibling=None):
    from arelle.ModelValue import (QName)
    from arelle.FunctionXs import (string)
    document = parent.ownerDocument
    documentElement = document.documentElement
    if isinstance(childName1, QName):
        child = document.createElementNS(childName1.namespaceURI, 
                                         addQnameValue(documentElement, childName1))
    else:   # called with namespaceURI, localName
        existingPrefix = xmlnsprefix(documentElement, childName1)
        if existingPrefix is None:  # assume prefix is used consistently and doesn't need cross-checking
            prefix, sep, localName = childName2.partition(":")
            if sep and localName:
                setXmlns(documentElement, prefix, childName1)
        child = document.createElementNS(childName1, childName2)
    if afterSibling:
        parent.insertBefore(child, afterSibling.nextSibling)
    else:
        parent.appendChild(child)
    if attributes:
        for name, value in (attributes if len(attributes) > 0 and isinstance(attributes[0],tuple) else (attributes,)):
            if isinstance(name,QName):
                child.setAttributeNS(name.namespaceURI,
                                     addQnameValue(document.documentElement, name),
                                     str(value))
            else:
                child.setAttribute(name, string(None, value) )
    if text:
        textNode = document.createTextNode(string(None, text))
        child.appendChild(textNode)
    return child
Example #2
0
def addChild(parent,
             childName1,
             childName2=None,
             attributes=None,
             text=None,
             afterSibling=None):
    from arelle.ModelValue import (QName)
    from arelle.FunctionXs import (string)
    document = parent.ownerDocument
    documentElement = document.documentElement
    if isinstance(childName1, QName):
        child = document.createElementNS(
            childName1.namespaceURI, addQnameValue(documentElement,
                                                   childName1))
    else:  # called with namespaceURI, localName
        existingPrefix = xmlnsprefix(documentElement, childName1)
        if existingPrefix is None:  # assume prefix is used consistently and doesn't need cross-checking
            prefix, sep, localName = childName2.partition(":")
            if sep and localName:
                setXmlns(documentElement, prefix, childName1)
        child = document.createElementNS(childName1, childName2)
    if afterSibling:
        parent.insertBefore(child, afterSibling.nextSibling)
    else:
        parent.appendChild(child)
    if attributes:
        for name, value in (attributes if len(attributes) > 0
                            and isinstance(attributes[0], tuple) else
                            (attributes, )):
            if isinstance(name, QName):
                child.setAttributeNS(
                    name.namespaceURI,
                    addQnameValue(document.documentElement, name), str(value))
            else:
                child.setAttribute(name, string(None, value))
    if text:
        textNode = document.createTextNode(string(None, text))
        child.appendChild(textNode)
    return child
Example #3
0
def produceOutputFact(xpCtx, formula, result):
    priorErrorCount = len(xpCtx.modelXbrl.errors)

    # assemble context
    conceptQname = aspectValue(xpCtx, formula, Aspect.CONCEPT,
                               "xbrlfe:missingConceptRule")
    if isinstance(conceptQname, VariableBindingError):
        xpCtx.modelXbrl.error(
            _("Formula {0} concept: {1}").format(formula, conceptQname.msg),
            "err", conceptQname.err)
        modelConcept = None
    else:
        modelConcept = xpCtx.modelXbrl.qnameConcepts[conceptQname]
        if modelConcept is None or not modelConcept.isItem:
            xpCtx.modelXbrl.error(
                _("Formula {0} concept {1} is not an item").format(
                    formula, conceptQname), "err", "xbrlfe:missingConceptRule")

    # entity
    entityIdentScheme = aspectValue(xpCtx, formula, Aspect.SCHEME,
                                    "xbrlfe:missingEntityIdentifierRule")
    if isinstance(entityIdentScheme, VariableBindingError):
        xpCtx.modelXbrl.error(
            _("Formula {0} entity identifier scheme: {1}").format(
                formula, entityIdentScheme.msg), "err", str(entityIdentScheme))
        entityIdentValue = None
    else:
        entityIdentValue = aspectValue(xpCtx, formula, Aspect.VALUE,
                                       "xbrlfe:missingEntityIdentifierRule")
        if isinstance(entityIdentValue, VariableBindingError):
            xpCtx.modelXbrl.error(
                _("Formula {0} entity identifier value: {1}").format(
                    formula, entityIdentValue.msg), "err",
                str(entityIdentScheme))

    # period
    periodType = aspectValue(xpCtx, formula, Aspect.PERIOD_TYPE,
                             "xbrlfe:missingPeriodRule")
    periodStart = None
    periodEndInstant = None
    if isinstance(periodType, VariableBindingError):
        xpCtx.modelXbrl.error(
            _("Formula {0} period type: {1}").format(formula, periodType.msg),
            "err", str(periodType))
    elif periodType == "instant":
        periodEndInstant = aspectValue(xpCtx, formula, Aspect.INSTANT,
                                       "xbrlfe:missingPeriodRule")
        if isinstance(periodEndInstant, VariableBindingError):
            xpCtx.modelXbrl.error(
                _("Formula {0} period start: {1}").format(
                    formula, periodEndInstant.msg), "err",
                str(periodEndInstant))
    elif periodType == "duration":
        periodStart = aspectValue(xpCtx, formula, Aspect.START,
                                  "xbrlfe:missingPeriodRule")
        if isinstance(periodStart, VariableBindingError):
            xpCtx.modelXbrl.error(
                _("Formula {0} period start: {1}").format(
                    formula, periodStart.msg), "err", str(periodStart))
        periodEndInstant = aspectValue(xpCtx, formula, Aspect.END,
                                       "xbrlfe:missingPeriodRule")
        if isinstance(periodEndInstant, VariableBindingError):
            xpCtx.modelXbrl.error(
                _("Formula {0} period end: {1}").format(
                    formula, periodEndInstant.msg), "err",
                str(periodEndInstant))

    # unit
    if modelConcept and modelConcept.isNumeric:
        unitSource = aspectValue(xpCtx, formula, Aspect.UNIT_MEASURES, None)
        multDivBy = aspectValue(xpCtx, formula, Aspect.MULTIPLY_BY,
                                "xbrlfe:missingUnitRule")
        if isinstance(multDivBy, VariableBindingError):
            xpCtx.modelXbrl.error(
                _("Formula {0} unit: {1}").format(formula,
                                                  multDivBy.msg), "err",
                str(multDivBy) if isinstance(multDivBy, VariableBindingError)
                else "xbrlfe:missingUnitRule")
            multiplyBy = ()
            divideBy = ()  # prevent errors later if bad
        else:
            divMultBy = aspectValue(xpCtx, formula, Aspect.DIVIDE_BY,
                                    "xbrlfe:missingUnitRule")
            if isinstance(divMultBy, VariableBindingError):
                xpCtx.modelXbrl.error(
                    _("Formula {0} unit: {1}").format(formula,
                                                      divMultBy.msg), "err",
                    str(multDivBy)
                    if isinstance(divMultBy, VariableBindingError) else
                    "xbrlfe:missingUnitRule")
                multiplyBy = ()
                divideBy = ()  # prevent errors later if bad
            else:
                multiplyBy = unitSource[0] + multDivBy[0] + divMultBy[1]
                divideBy = unitSource[1] + multDivBy[1] + divMultBy[0]
                # remove cancelling mult/div units
                lookForCommonUnits = True
                while lookForCommonUnits:
                    lookForCommonUnits = False
                    for commonUnit in multiplyBy:
                        if commonUnit in divideBy:
                            multiplyBy.remove(commonUnit)
                            divideBy.remove(commonUnit)
                            lookForCommonUnits = True
                            break
                if len(multiplyBy) == 0:  # if no units add pure
                    multiplyBy.append(XbrlConst.qnXbrliPure)

    # dimensions
    segOCCs = []
    scenOCCs = []
    if formula.aspectModel == "dimensional":
        dimAspects = {}
        dimQnames = aspectValue(xpCtx, formula, Aspect.DIMENSIONS, None)
        if dimQnames:
            for dimQname in dimQnames:
                dimConcept = xpCtx.modelXbrl.qnameConcepts[dimQname]
                dimErr = "xbrlfe:missing{0}DimensionRule".format(
                    "typed" if dimConcept and dimConcept.isTypedDimension else
                    "explicit")
                dimValue = aspectValue(xpCtx, formula, dimQname, dimErr)
                if isinstance(dimValue, VariableBindingError):
                    xpCtx.modelXbrl.error(
                        _("Formula {0} dimension {1}: {2}").format(
                            formula, dimQname, dimValue.msg), "err", dimErr)
                elif dimValue and xpCtx.modelXbrl.qnameDimensionDefaults.get(
                        dimQname) != dimValue:
                    dimAspects[dimQname] = dimValue
        segOCCs = aspectValue(xpCtx, formula, Aspect.NON_XDT_SEGMENT, None)
        scenOCCs = aspectValue(xpCtx, formula, Aspect.NON_XDT_SCENARIO, None)
    else:
        dimAspects = None  # non-dimensional
        segOCCs = aspectValue(xpCtx, formula, Aspect.COMPLETE_SEGMENT, None)
        scenOCCs = aspectValue(xpCtx, formula, Aspect.COMPLETE_SCENARIO, None)

    if priorErrorCount < len(xpCtx.modelXbrl.errors):
        return None  # had errors, don't produce output fact

    # does context exist in out instance document
    outputInstanceQname = formula.outputInstanceQname
    outputXbrlInstance = xpCtx.inScopeVars[outputInstanceQname]
    xbrlElt = outputXbrlInstance.modelDocument.xmlRootElement

    # in source instance document

    # add context
    prevCntx = outputXbrlInstance.matchContext(entityIdentScheme,
                                               entityIdentValue, periodType,
                                               periodStart, periodEndInstant,
                                               dimAspects, segOCCs, scenOCCs)
    if prevCntx:
        cntxId = prevCntx.id
        newCntxElt = prevCntx.element
    else:
        cntxId = 'c-{0:02n}'.format(len(outputXbrlInstance.contexts) + 1)
        newCntxElt = XmlUtil.addChild(
            xbrlElt,
            XbrlConst.xbrli,
            "context",
            attributes=("id", cntxId),
            afterSibling=xpCtx.outputLastContext.get(outputInstanceQname))
        xpCtx.outputLastContext[outputInstanceQname] = newCntxElt
        entityElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "entity")
        XmlUtil.addChild(entityElt,
                         XbrlConst.xbrli,
                         "identifier",
                         attributes=("scheme", entityIdentScheme),
                         text=entityIdentValue)
        periodElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "period")
        if periodType == "forever":
            XmlUtil.addChild(periodElt, XbrlConst.xbrli, "forever")
        elif periodType == "instant":
            XmlUtil.addChild(periodElt,
                             XbrlConst.xbrli,
                             "instant",
                             text=XmlUtil.dateunionValue(periodEndInstant,
                                                         subtractOneDay=True))
        elif periodType == "duration":
            XmlUtil.addChild(periodElt,
                             XbrlConst.xbrli,
                             "startDate",
                             text=XmlUtil.dateunionValue(periodStart))
            XmlUtil.addChild(periodElt,
                             XbrlConst.xbrli,
                             "endDate",
                             text=XmlUtil.dateunionValue(periodEndInstant,
                                                         subtractOneDay=True))
        segmentElt = None
        scenarioElt = None
        from arelle.ModelObject import ModelDimensionValue
        if dimAspects:
            for dimQname in sorted(dimAspects.keys()):
                dimValue = dimAspects[dimQname]
                if isinstance(dimValue, ModelDimensionValue):
                    if dimValue.isExplicit:
                        dimMemberQname = dimValue.memberQname
                    contextEltName = dimValue.contextElement
                else:  # qname for explicit or node for typed
                    dimMemberQname = dimValue
                    contextEltName = xpCtx.modelXbrl.qnameDimensionContextElement.get(
                        dimQname)
                if contextEltName == "segment":
                    if not segmentElt:
                        segmentElt = XmlUtil.addChild(entityElt,
                                                      XbrlConst.xbrli,
                                                      "segment")
                    contextElt = segmentElt
                elif contextEltName == "scenario":
                    if not scenarioElt:
                        scenarioElt = XmlUtil.addChild(newCntxElt,
                                                       XbrlConst.xbrli,
                                                       "scenario")
                    contextElt = scenarioElt
                else:
                    continue
                dimConcept = xpCtx.modelXbrl.qnameConcepts[dimQname]
                dimAttr = ("dimension",
                           XmlUtil.addQnameValue(xbrlElt, dimConcept.qname))
                if dimConcept.isTypedDimension:
                    dimElt = XmlUtil.addChild(contextElt,
                                              XbrlConst.xbrldi,
                                              "typedMember",
                                              attributes=dimAttr)
                    if isinstance(dimValue,
                                  ModelDimensionValue) and dimValue.isTyped:
                        XmlUtil.copyChildren(dimElt, dimValue.typedMember)
                elif dimMemberQname:
                    dimElt = XmlUtil.addChild(contextElt,
                                              XbrlConst.xbrldi,
                                              "explicitMember",
                                              attributes=dimAttr,
                                              text=XmlUtil.addQnameValue(
                                                  xbrlElt, dimMemberQname))
        if segOCCs:
            if not segmentElt:
                segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli,
                                              "segment")
            XmlUtil.copyNodes(segmentElt, segOCCs)
        if scenOCCs:
            if not scenarioElt:
                scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli,
                                               "scenario")
            XmlUtil.copyNodes(scenarioElt, scenOCCs)

        outputXbrlInstance.modelDocument.contextDiscover(newCntxElt)

    # does unit exist

    # add unit
    if modelConcept.isNumeric:
        prevUnit = outputXbrlInstance.matchUnit(multiplyBy, divideBy)
        if prevUnit:
            unitId = prevUnit.id
            newUnitElt = prevUnit.element
        else:
            unitId = 'u-{0:02n}'.format(len(outputXbrlInstance.units) + 1)
            newUnitElt = XmlUtil.addChild(
                xbrlElt,
                XbrlConst.xbrli,
                "unit",
                attributes=("id", unitId),
                afterSibling=xpCtx.outputLastUnit.get(outputInstanceQname))
            xpCtx.outputLastUnit[outputInstanceQname] = newUnitElt
            if len(divideBy) == 0:
                for multiply in multiplyBy:
                    XmlUtil.addChild(newUnitElt,
                                     XbrlConst.xbrli,
                                     "measure",
                                     text=XmlUtil.addQnameValue(
                                         xbrlElt, multiply))
            else:
                divElt = XmlUtil.addChild(newUnitElt, XbrlConst.xbrli,
                                          "divide")
                numElt = XmlUtil.addChild(divElt, XbrlConst.xbrli,
                                          "unitNumerator")
                denElt = XmlUtil.addChild(divElt, XbrlConst.xbrli,
                                          "unitDenominator")
                for multiply in multiplyBy:
                    XmlUtil.addChild(numElt,
                                     XbrlConst.xbrli,
                                     "measure",
                                     text=XmlUtil.addQnameValue(
                                         xbrlElt, multiply))
                for divide in divideBy:
                    XmlUtil.addChild(denElt,
                                     XbrlConst.xbrli,
                                     "measure",
                                     text=XmlUtil.addQnameValue(
                                         xbrlElt, divide))
            outputXbrlInstance.modelDocument.unitDiscover(newUnitElt)

    # add fact
    attrs = [("contextRef", cntxId)]
    precision = None
    decimals = None
    if modelConcept.isNumeric:
        attrs.append(("unitRef", unitId))
    value = formula.evaluate(xpCtx)
    valueSeqLen = len(value)
    if valueSeqLen > 1:
        xpCtx.modelXbrl.error(
            _("Formula {0} value is a sequence of length {1}").format(
                formula, valueSeqLen), "err", "xbrlfe:nonSingletonOutputValue")
    else:
        if valueSeqLen == 0:  #xsi:nil if no value
            attrs.append((XbrlConst.qnXsiNil, "true"))
            v = None
        else:
            # add precision/decimals for non-fraction numerics
            if modelConcept.isNumeric and not modelConcept.isFraction:
                if formula.hasDecimals:
                    decimals = formula.evaluateRule(xpCtx, Aspect.DECIMALS)
                    attrs.append(("decimals", decimals))
                else:
                    if formula.hasPrecision:
                        precision = formula.evaluateRule(
                            xpCtx, Aspect.PRECISION)
                    else:
                        precision = 0
                    attrs.append(("precision", precision))

            x = value[0]
            if isinstance(x, float):
                from math import (log10, isnan, isinf, fabs)
                if (isnan(x)
                        or (precision and (isinf(precision) or precision == 0))
                        or (decimals and isinf(decimals))):
                    v = string(xpCtx, x)
                elif decimals is not None:
                    v = "%.*f" % (int(decimals), x)
                elif precision is not None:
                    a = fabs(x)
                    log = log10(a) if a != 0 else 0
                    v = "%.*f" % (int(precision) - int(log) -
                                  (1 if a >= 1 else 0), x)
                else:  # no implicit precision yet
                    v = string(xpCtx, x)
            elif isinstance(x, QName):
                v = XmlUtil.addQnameValue(xbrlElt, x)
            elif isinstance(x, datetime.datetime):
                v = XmlUtil.dateunionValue(x)
            else:
                v = string(xpCtx, x)
        itemElt = XmlUtil.addChild(
            xbrlElt,
            conceptQname,
            attributes=attrs,
            text=v,
            afterSibling=xpCtx.outputLastFact.get(outputInstanceQname))
        xpCtx.outputLastFact[outputInstanceQname] = itemElt
        newFact = outputXbrlInstance.modelDocument.factDiscover(
            itemElt, outputXbrlInstance.facts)
        return newFact
Example #4
0
def produceOutputFact(xpCtx, formula, result):
    priorErrorCount = len(xpCtx.modelXbrl.errors)
    
    # assemble context
    conceptQname = aspectValue(xpCtx, formula, Aspect.CONCEPT, "xbrlfe:missingConceptRule")
    if isinstance(conceptQname, VariableBindingError):
        xpCtx.modelXbrl.error( _("Formula {0} concept: {1}").format( formula, conceptQname.msg),
                "err", conceptQname.err)
        modelConcept = None
    else:
        modelConcept = xpCtx.modelXbrl.qnameConcepts[conceptQname]
        if modelConcept is None or not modelConcept.isItem:
            xpCtx.modelXbrl.error( _("Formula {0} concept {1} is not an item").format( formula, conceptQname),
                    "err", "xbrlfe:missingConceptRule")
        
    # entity
    entityIdentScheme = aspectValue(xpCtx, formula, Aspect.SCHEME, "xbrlfe:missingEntityIdentifierRule")
    if isinstance(entityIdentScheme, VariableBindingError):
        xpCtx.modelXbrl.error( _("Formula {0} entity identifier scheme: {1}").format( formula, entityIdentScheme.msg ),
                "err", str(entityIdentScheme))
        entityIdentValue = None
    else:
        entityIdentValue = aspectValue(xpCtx, formula, Aspect.VALUE, "xbrlfe:missingEntityIdentifierRule")
        if isinstance(entityIdentValue, VariableBindingError):
            xpCtx.modelXbrl.error( _("Formula {0} entity identifier value: {1}").format( formula, entityIdentValue.msg ),
                    "err", str(entityIdentScheme))
    
    # period
    periodType = aspectValue(xpCtx, formula, Aspect.PERIOD_TYPE, "xbrlfe:missingPeriodRule")
    periodStart = None
    periodEndInstant = None
    if isinstance(periodType, VariableBindingError):
        xpCtx.modelXbrl.error( _("Formula {0} period type: {1}").format( formula, periodType.msg ),
                "err", str(periodType))
    elif periodType == "instant":
        periodEndInstant = aspectValue(xpCtx, formula, Aspect.INSTANT, "xbrlfe:missingPeriodRule")
        if isinstance(periodEndInstant, VariableBindingError):
            xpCtx.modelXbrl.error( _("Formula {0} period start: {1}").format( formula, periodEndInstant.msg ),
                    "err", str(periodEndInstant))
    elif periodType == "duration":
        periodStart = aspectValue(xpCtx, formula, Aspect.START, "xbrlfe:missingPeriodRule")
        if isinstance(periodStart, VariableBindingError):
            xpCtx.modelXbrl.error( _("Formula {0} period start: {1}").format( formula, periodStart.msg ),
                    "err", str(periodStart))
        periodEndInstant = aspectValue(xpCtx, formula, Aspect.END, "xbrlfe:missingPeriodRule")
        if isinstance(periodEndInstant, VariableBindingError):
            xpCtx.modelXbrl.error( _("Formula {0} period end: {1}").format( formula, periodEndInstant.msg ),
                    "err", str(periodEndInstant))
        
    # unit
    if modelConcept and modelConcept.isNumeric:
        unitSource = aspectValue(xpCtx, formula, Aspect.UNIT_MEASURES, None)
        multDivBy = aspectValue(xpCtx, formula, Aspect.MULTIPLY_BY, "xbrlfe:missingUnitRule")
        if isinstance(multDivBy, VariableBindingError):
            xpCtx.modelXbrl.error( _("Formula {0} unit: {1}").format( formula, multDivBy.msg ),
                    "err", str(multDivBy) if isinstance(multDivBy, VariableBindingError) else "xbrlfe:missingUnitRule")
            multiplyBy = (); divideBy = () # prevent errors later if bad
        else:
            divMultBy = aspectValue(xpCtx, formula, Aspect.DIVIDE_BY, "xbrlfe:missingUnitRule")
            if isinstance(divMultBy, VariableBindingError):
                xpCtx.modelXbrl.error( _("Formula {0} unit: {1}").format( formula, divMultBy.msg ),
                        "err", str(multDivBy) if isinstance(divMultBy, VariableBindingError) else "xbrlfe:missingUnitRule")
                multiplyBy = (); divideBy = () # prevent errors later if bad
            else:
                multiplyBy = unitSource[0] + multDivBy[0] + divMultBy[1]
                divideBy = unitSource[1] + multDivBy[1] + divMultBy[0]
                # remove cancelling mult/div units
                lookForCommonUnits = True
                while lookForCommonUnits:
                    lookForCommonUnits = False
                    for commonUnit in multiplyBy:
                        if commonUnit in divideBy:
                            multiplyBy.remove(commonUnit)
                            divideBy.remove(commonUnit)
                            lookForCommonUnits = True
                            break
                if len(multiplyBy) == 0: # if no units add pure
                    multiplyBy.append(XbrlConst.qnXbrliPure)
                        
    
    # dimensions
    segOCCs = []
    scenOCCs = []
    if formula.aspectModel == "dimensional":
        dimAspects = {}
        dimQnames = aspectValue(xpCtx, formula, Aspect.DIMENSIONS, None)
        if dimQnames:
            for dimQname in dimQnames:
                dimConcept = xpCtx.modelXbrl.qnameConcepts[dimQname]
                dimErr = "xbrlfe:missing{0}DimensionRule".format("typed" if dimConcept and dimConcept.isTypedDimension else "explicit")
                dimValue = aspectValue(xpCtx, formula, dimQname, dimErr)
                if isinstance(dimValue, VariableBindingError):
                    xpCtx.modelXbrl.error( _("Formula {0} dimension {1}: {2}").format( formula, dimQname, dimValue.msg ),
                            "err", dimErr)
                elif dimValue and xpCtx.modelXbrl.qnameDimensionDefaults.get(dimQname) != dimValue:
                    dimAspects[dimQname] = dimValue
        segOCCs = aspectValue(xpCtx, formula, Aspect.NON_XDT_SEGMENT, None)
        scenOCCs = aspectValue(xpCtx, formula, Aspect.NON_XDT_SCENARIO, None)
    else:
        dimAspects = None   # non-dimensional
        segOCCs = aspectValue(xpCtx, formula, Aspect.COMPLETE_SEGMENT, None)
        scenOCCs = aspectValue(xpCtx, formula, Aspect.COMPLETE_SCENARIO, None)
                    
    if priorErrorCount < len(xpCtx.modelXbrl.errors):
        return None # had errors, don't produce output fact
    
    # does context exist in out instance document
    outputInstanceQname = formula.outputInstanceQname
    outputXbrlInstance = xpCtx.inScopeVars[outputInstanceQname]
    xbrlElt = outputXbrlInstance.modelDocument.xmlRootElement
    
    # in source instance document
    
    # add context
    prevCntx = outputXbrlInstance.matchContext(
         entityIdentScheme, entityIdentValue, periodType, periodStart, periodEndInstant, 
         dimAspects, segOCCs, scenOCCs)
    if prevCntx:
        cntxId = prevCntx.id
        newCntxElt = prevCntx.element
    else:
        cntxId = 'c-{0:02n}'.format( len(outputXbrlInstance.contexts) + 1)
        newCntxElt = XmlUtil.addChild(xbrlElt, XbrlConst.xbrli, "context", attributes=("id", cntxId),
                                      afterSibling=xpCtx.outputLastContext.get(outputInstanceQname))
        xpCtx.outputLastContext[outputInstanceQname] = newCntxElt
        entityElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "entity")
        XmlUtil.addChild(entityElt, XbrlConst.xbrli, "identifier",
                            attributes=("scheme", entityIdentScheme),
                            text=entityIdentValue)
        periodElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "period")
        if periodType == "forever":
            XmlUtil.addChild(periodElt, XbrlConst.xbrli, "forever")
        elif periodType == "instant":
            XmlUtil.addChild(periodElt, XbrlConst.xbrli, "instant", 
                             text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True))
        elif periodType == "duration":
            XmlUtil.addChild(periodElt, XbrlConst.xbrli, "startDate", 
                             text=XmlUtil.dateunionValue(periodStart))
            XmlUtil.addChild(periodElt, XbrlConst.xbrli, "endDate", 
                             text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True))
        segmentElt = None
        scenarioElt = None
        from arelle.ModelObject import ModelDimensionValue
        if dimAspects:
            for dimQname in sorted(dimAspects.keys()):
                dimValue = dimAspects[dimQname]
                if isinstance(dimValue, ModelDimensionValue):
                    if dimValue.isExplicit: 
                        dimMemberQname = dimValue.memberQname
                    contextEltName = dimValue.contextElement
                else: # qname for explicit or node for typed
                    dimMemberQname = dimValue
                    contextEltName = xpCtx.modelXbrl.qnameDimensionContextElement.get(dimQname)
                if contextEltName == "segment":
                    if not segmentElt: 
                        segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment")
                    contextElt = segmentElt
                elif contextEltName == "scenario":
                    if not scenarioElt: 
                        scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario")
                    contextElt = scenarioElt
                else:
                    continue
                dimConcept = xpCtx.modelXbrl.qnameConcepts[dimQname]
                dimAttr = ("dimension", XmlUtil.addQnameValue(xbrlElt, dimConcept.qname))
                if dimConcept.isTypedDimension:
                    dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "typedMember", 
                                              attributes=dimAttr)
                    if isinstance(dimValue, ModelDimensionValue) and dimValue.isTyped:
                        XmlUtil.copyChildren(dimElt, dimValue.typedMember)
                elif dimMemberQname:
                    dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "explicitMember",
                                              attributes=dimAttr,
                                              text=XmlUtil.addQnameValue(xbrlElt, dimMemberQname))
        if segOCCs:
            if not segmentElt: 
                segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment")
            XmlUtil.copyNodes(segmentElt, segOCCs)
        if scenOCCs:
            if not scenarioElt: 
                scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario")
            XmlUtil.copyNodes(scenarioElt, scenOCCs)
                
        outputXbrlInstance.modelDocument.contextDiscover(newCntxElt)
    
    # does unit exist
    
    # add unit
    if modelConcept.isNumeric:
        prevUnit = outputXbrlInstance.matchUnit(multiplyBy, divideBy)
        if prevUnit:
            unitId = prevUnit.id
            newUnitElt = prevUnit.element
        else:
            unitId = 'u-{0:02n}'.format( len(outputXbrlInstance.units) + 1)
            newUnitElt = XmlUtil.addChild(xbrlElt, XbrlConst.xbrli, "unit", attributes=("id", unitId),
                                          afterSibling=xpCtx.outputLastUnit.get(outputInstanceQname))
            xpCtx.outputLastUnit[outputInstanceQname] = newUnitElt
            if len(divideBy) == 0:
                for multiply in multiplyBy:
                    XmlUtil.addChild(newUnitElt, XbrlConst.xbrli, "measure", text=XmlUtil.addQnameValue(xbrlElt, multiply))
            else:
                divElt = XmlUtil.addChild(newUnitElt, XbrlConst.xbrli, "divide")
                numElt = XmlUtil.addChild(divElt, XbrlConst.xbrli, "unitNumerator")
                denElt = XmlUtil.addChild(divElt, XbrlConst.xbrli, "unitDenominator")
                for multiply in multiplyBy:
                    XmlUtil.addChild(numElt, XbrlConst.xbrli, "measure", text=XmlUtil.addQnameValue(xbrlElt, multiply))
                for divide in divideBy:
                    XmlUtil.addChild(denElt, XbrlConst.xbrli, "measure", text=XmlUtil.addQnameValue(xbrlElt, divide))
            outputXbrlInstance.modelDocument.unitDiscover(newUnitElt)
    
    # add fact
    attrs = [("contextRef", cntxId)]
    precision = None
    decimals = None
    if modelConcept.isNumeric:
        attrs.append(("unitRef", unitId))
    value = formula.evaluate(xpCtx)
    valueSeqLen = len(value)
    if valueSeqLen > 1:
        xpCtx.modelXbrl.error( _("Formula {0} value is a sequence of length {1}").format( formula, valueSeqLen ),
                "err", "xbrlfe:nonSingletonOutputValue")
    else: 
        if valueSeqLen == 0: #xsi:nil if no value
            attrs.append((XbrlConst.qnXsiNil, "true"))
            v = None
        else:
            # add precision/decimals for non-fraction numerics
            if modelConcept.isNumeric and not modelConcept.isFraction:
                if formula.hasDecimals:
                    decimals = formula.evaluateRule(xpCtx, Aspect.DECIMALS)
                    attrs.append(("decimals", decimals))
                else:
                    if formula.hasPrecision:
                        precision = formula.evaluateRule(xpCtx, Aspect.PRECISION)
                    else:
                        precision = 0
                    attrs.append(("precision", precision))
                    
            x = value[0]
            if isinstance(x,float):
                from math import (log10, isnan, isinf, fabs)
                if (isnan(x) or
                    (precision and (isinf(precision) or precision == 0)) or 
                    (decimals and isinf(decimals))):
                    v = string(xpCtx, x)
                elif decimals is not None:
                    v = "%.*f" % ( int(decimals), x)
                elif precision is not None:
                    a = fabs(x)
                    log = log10(a) if a != 0 else 0
                    v = "%.*f" % ( int(precision) - int(log) - (1 if a >= 1 else 0), x)
                else: # no implicit precision yet
                    v = string(xpCtx, x)
            elif isinstance(x,QName):
                v = XmlUtil.addQnameValue(xbrlElt, x)
            elif isinstance(x,datetime.datetime):
                v = XmlUtil.dateunionValue(x)
            else:
                v = string(xpCtx, x)
        itemElt = XmlUtil.addChild(xbrlElt, conceptQname,
                                   attributes=attrs, text=v,
                                   afterSibling=xpCtx.outputLastFact.get(outputInstanceQname))
        xpCtx.outputLastFact[outputInstanceQname] = itemElt
        newFact = outputXbrlInstance.modelDocument.factDiscover(itemElt, outputXbrlInstance.facts)
        return newFact