def expandDefinition(view, structuralNode, breakdownNode, definitionNode, depth, axisDisposition, facts, i=None, tblAxisRels=None, processOpenDefinitionNode=True):
    subtreeRelationships = view.axisSubtreeRelSet.fromModelObject(definitionNode)
    
    def checkLabelWidth(structuralNode, checkBoundFact=False):
        if axisDisposition == "y":
            # messages can't be evaluated, just use the text portion of format string
            label = structuralNode.header(lang=view.lang, 
                                          returnGenLabel=not checkBoundFact, 
                                          returnMsgFormatString=not checkBoundFact)
            if label:
                # need to et more exact word length in screen units
                widestWordLen = max(len(w) * RENDER_UNITS_PER_CHAR for w in label.split())
                # abstract only pertains to subtree of closed nodesbut not cartesian products or open nodes
                while structuralNode.depth >= len(view.rowHdrColWidth):
                    view.rowHdrColWidth.append(0)
                if definitionNode.isAbstract or not subtreeRelationships: # isinstance(definitionNode, ModelOpenDefinitionNode):                    
                    if widestWordLen > view.rowHdrColWidth[structuralNode.depth]:
                        view.rowHdrColWidth[structuralNode.depth] = widestWordLen
                else:
                    if widestWordLen > view.rowNonAbstractHdrSpanMin[structuralNode.depth]:
                        view.rowNonAbstractHdrSpanMin[structuralNode.depth] = widestWordLen
                        
    if axisDisposition == "z" and structuralNode.aspects is None:
        structuralNode.aspects = view.zOrdinateChoices.get(definitionNode, None)
    if structuralNode and isinstance(definitionNode, (ModelBreakdown, ModelEuAxisCoord, ModelDefinitionNode)):
        try:
            
            #cartesianProductNestedArgs = (view, depth, axisDisposition, facts, tblAxisRels, i)
            ordCardinality, ordDepth = definitionNode.cardinalityAndDepth(structuralNode)
            if (not definitionNode.isAbstract and
                isinstance(definitionNode, ModelClosedDefinitionNode) and 
                ordCardinality == 0):
                view.modelXbrl.error("xbrlte:closedDefinitionNodeZeroCardinality",
                    _("Closed definition node %(xlinkLabel)s does not contribute at least one structural node"),
                    modelObject=(view.modelTable,definitionNode), xlinkLabel=definitionNode.xlinkLabel, axis=definitionNode.localName)
            nestedDepth = depth + ordDepth
            # HF test
            cartesianProductNestedArgs = [view, nestedDepth, axisDisposition, facts, tblAxisRels, i]
            if axisDisposition == "z":
                if depth == 1: # choices (combo boxes) don't add to z row count
                    view.zAxisRows += 1 
            elif axisDisposition == "x":
                if ordDepth:
                    if nestedDepth - 1 > view.colHdrRows:
                        view.colHdrRows = nestedDepth - 1
                    '''
                    if not view.colHdrDocRow:
                        if definitionNode.header(role="http://www.xbrl.org/2008/role/documentation",
                                                       lang=view.lang): 
                            view.colHdrDocRow = True
                    if not view.colHdrCodeRow:
                        if definitionNode.header(role="http://www.eurofiling.info/role/2010/coordinate-code"): 
                            view.colHdrCodeRow = True
                    '''
                hdrNonStdRoles = view.colHdrNonStdRoles
            elif axisDisposition == "y":
                if ordDepth:
                    #if not definitionNode.isAbstract:
                    #    view.dataRows += ordCardinality
                    if nestedDepth - 1 > view.rowHdrCols: 
                        view.rowHdrCols = nestedDepth - 1
                        for j in range(1 + ordDepth):
                            view.rowHdrColWidth.append(RENDER_UNITS_PER_CHAR)  # min width for 'tail' of nonAbstract coordinate
                            view.rowNonAbstractHdrSpanMin.append(0)
                    checkLabelWidth(structuralNode, checkBoundFact=False)
                    ''' 
                    if not view.rowHdrDocCol:
                        if definitionNode.header(role="http://www.xbrl.org/2008/role/documentation",
                                             lang=view.lang): 
                            view.rowHdrDocCol = True
                    if not view.rowHdrCodeCol:
                        if definitionNode.header(role="http://www.eurofiling.info/role/2010/coordinate-code"): 
                            view.rowHdrCodeCol = True
                    '''
                hdrNonStdRoles = view.rowHdrNonStdRoles
            if axisDisposition in ("x", "y"):
                hdrNonStdPosition = -1  # where a match last occured
                for rel in view.modelXbrl.relationshipSet(XbrlConst.elementLabel).fromModelObject(definitionNode):
                    if rel.toModelObject is not None and rel.toModelObject.role != XbrlConst.genStandardLabel:
                        labelLang = rel.toModelObject.xmlLang
                        labelRole = rel.toModelObject.role
                        if (labelLang == view.lang or labelLang.startswith(view.lang) or view.lang.startswith(labelLang)
                            or ("code" in labelRole)):
                            labelRole = rel.toModelObject.role
                            if labelRole in hdrNonStdRoles:
                                hdrNonStdPosition = hdrNonStdRoles.index(labelRole)
                            else:
                                hdrNonStdRoles.insert(hdrNonStdPosition + 1, labelRole)
            isCartesianProductExpanded = False
            if not isinstance(definitionNode, ModelFilterDefinitionNode):
                isCartesianProductExpanded = True
                # note: reduced set of facts should always be passed to subsequent open nodes
                for axisSubtreeRel in subtreeRelationships:
                    childDefinitionNode = axisSubtreeRel.toModelObject
                    if childDefinitionNode.isRollUp:
                        structuralNode.rollUpStructuralNode = StructuralNode(structuralNode, breakdownNode, childDefinitionNode, )
                        if not structuralNode.childStructuralNodes: # first sub ordinate is the roll up
                            structuralNode.subtreeRollUp = CHILD_ROLLUP_FIRST
                        else: 
                            structuralNode.subtreeRollUp = CHILD_ROLLUP_LAST
                        if not view.topRollup.get(axisDisposition):
                            view.topRollup[axisDisposition] = structuralNode.subtreeRollUp
                    else:
                        if (isinstance(definitionNode, (ModelBreakdown, ModelCompositionDefinitionNode)) and
                            isinstance(childDefinitionNode, ModelRelationshipDefinitionNode)): # append list products to composititionAxes subObjCntxs
                            childStructuralNode = structuralNode
                        else:
                            childStructuralNode = StructuralNode(structuralNode, breakdownNode, childDefinitionNode) # others are nested structuralNode
                            if axisDisposition != "z":
                                structuralNode.childStructuralNodes.append(childStructuralNode)
                        if axisDisposition != "z":
                            expandDefinition(view, childStructuralNode, breakdownNode, childDefinitionNode, depth+ordDepth, axisDisposition, facts, i, tblAxisRels) #recurse
                            if not childContainsOpenNodes(childStructuralNode) :
                                # To be computed only if the structural node does not contain an open node
                                cartesianProductExpander(childStructuralNode, *cartesianProductNestedArgs)
                        else:
                            childStructuralNode.indent = depth - 1
                            if structuralNode.choiceStructuralNodes is not None:
                                structuralNode.choiceStructuralNodes.append(childStructuralNode)
                            expandDefinition(view, childStructuralNode, breakdownNode, childDefinitionNode, depth + 1, axisDisposition, facts) #recurse
                    # required when switching from abstract to roll up to determine abstractness
                    #if not structuralNode.subtreeRollUp and structuralNode.childStructuralNodes and definitionNode.tag.endswith("Node"):
                    #    structuralNode.subtreeRollUp = CHILDREN_BUT_NO_ROLLUP
                    
            #if not hasattr(structuralNode, "indent"): # probably also for multiple open axes
            if processOpenDefinitionNode:
                if isinstance(definitionNode, ModelRelationshipDefinitionNode):
                    structuralNode.isLabeled = False
                    selfStructuralNodes = {} if definitionNode.axis.endswith('-or-self') else None
                    for rel in definitionNode.relationships(structuralNode):
                        if not isinstance(rel, list):
                            relChildStructuralNode = addRelationship(breakdownNode, definitionNode, rel, structuralNode, cartesianProductNestedArgs, selfStructuralNodes)
                        else:
                            addRelationships(breakdownNode, definitionNode, rel, relChildStructuralNode, cartesianProductNestedArgs)
                    if axisDisposition == "z":
                        # if definitionNode is first structural node child remove it
                        if structuralNode.choiceStructuralNodes and structuralNode.choiceStructuralNodes[0].definitionNode == definitionNode:
                            del structuralNode.choiceStructuralNodes[0]
                        # flatten hierarchy of nested structural nodes inot choice nodes (for single listbox)
                        def flattenChildNodesToChoices(childStructuralNodes, indent):
                            while childStructuralNodes:
                                choiceStructuralNode = childStructuralNodes.pop(0)
                                choiceStructuralNode.indent = indent
                                structuralNode.choiceStructuralNodes.append(choiceStructuralNode)
                                flattenChildNodesToChoices(choiceStructuralNode.childStructuralNodes, indent + 1)
                        if structuralNode.childStructuralNodes:
                            flattenChildNodesToChoices(structuralNode.childStructuralNodes, 0)
                    # set up by definitionNode.relationships
                    if isinstance(definitionNode, ModelConceptRelationshipDefinitionNode):
                        if (definitionNode._sourceQname != XbrlConst.qnXfiRoot and
                            definitionNode._sourceQname not in view.modelXbrl.qnameConcepts):
                            view.modelXbrl.error("xbrlte:invalidConceptRelationshipSource",
                                _("Concept relationship rule node %(xlinkLabel)s source %(source)s does not refer to an existing concept."),
                                modelObject=definitionNode, xlinkLabel=definitionNode.xlinkLabel, source=definitionNode._sourceQname)
                    elif isinstance(definitionNode, ModelDimensionRelationshipDefinitionNode):
                        dim = view.modelXbrl.qnameConcepts.get(definitionNode._dimensionQname)
                        if dim is None or not dim.isExplicitDimension:
                            view.modelXbrl.error("xbrlte:invalidExplicitDimensionQName",
                                _("Dimension relationship rule node %(xlinkLabel)s dimension %(dimension)s does not refer to an existing explicit dimension."),
                                modelObject=definitionNode, xlinkLabel=definitionNode.xlinkLabel, dimension=definitionNode._dimensionQname)
                        domMbr = view.modelXbrl.qnameConcepts.get(definitionNode._sourceQname)
                        if domMbr is None or not domMbr.isDomainMember:
                            view.modelXbrl.error("xbrlte:invalidDimensionRelationshipSource",
                                _("Dimension relationship rule node %(xlinkLabel)s source %(source)s does not refer to an existing domain member."),
                                modelObject=definitionNode, xlinkLabel=definitionNode.xlinkLabel, source=definitionNode._sourceQname)
                    if (definitionNode._axis in ("child", "child-or-self", "parent", "parent-or-self", "sibling", "sibling-or-self") and
                        (not isinstance(definitionNode._generations, _NUM_TYPES) or definitionNode._generations > 1)):
                        view.modelXbrl.error("xbrlte:relationshipNodeTooManyGenerations ",
                            _("Relationship rule node %(xlinkLabel)s formulaAxis %(axis)s implies a single generation tree walk but generations %(generations)s is greater than one."),
                            modelObject=definitionNode, xlinkLabel=definitionNode.xlinkLabel, axis=definitionNode._axis, generations=definitionNode._generations)
                    
                elif isinstance(definitionNode, ModelSelectionDefinitionNode):
                    structuralNode.setHasOpenNode()
                    structuralNode.isLabeled = False
                    isCartesianProductExpanded = True
                    varQn = definitionNode.variableQname
                    if varQn:
                        selections = sorted(structuralNode.evaluate(definitionNode, definitionNode.evaluate) or [], 
                                            key=lambda obj:sortkey(obj))
                        if isinstance(selections, (list,set,tuple)) and len(selections) > 1:
                            for selection in selections: # nested choices from selection list
                                childStructuralNode = StructuralNode(structuralNode, breakdownNode, definitionNode, contextItemFact=selection)
                                childStructuralNode.variables[varQn] = selection
                                childStructuralNode.indent = 0
                                if axisDisposition == "z":
                                    structuralNode.choiceStructuralNodes.append(childStructuralNode)
                                    childStructuralNode.zSelection = True
                                else:
                                    structuralNode.childStructuralNodes.append(childStructuralNode)
                                    expandDefinition(view, childStructuralNode, breakdownNode, definitionNode, depth, axisDisposition, facts, processOpenDefinitionNode=False) #recurse
                                    cartesianProductExpander(childStructuralNode, *cartesianProductNestedArgs)
                        else:
                            structuralNode.variables[varQn] = selections
                elif isinstance(definitionNode, ModelFilterDefinitionNode):
                    structuralNode.setHasOpenNode()
                    structuralNode.isLabeled = False
                    isCartesianProductExpanded = True
                    structuralNode.abstract = True # spanning ordinate acts as a subtitle
                    filteredFactsPartitions = structuralNode.evaluate(definitionNode, 
                                                                      definitionNode.filteredFactsPartitions, 
                                                                      evalArgs=(facts,))
                    if structuralNode._rendrCntx.formulaOptions.traceVariableFilterWinnowing:
                        view.modelXbrl.info("table:trace",
                            _("Filter node %(xlinkLabel)s facts partitions: %(factsPartitions)s"), 
                            modelObject=definitionNode, xlinkLabel=definitionNode.xlinkLabel,
                            factsPartitions=str(filteredFactsPartitions))
                        
                    # ohly for fact entry (true if no parent open nodes or all are on entry prototype row)
                    if axisDisposition != "z":
                        childList = structuralNode.childStructuralNodes
                        if structuralNode.isEntryPrototype(default=True):
                            for i in range(getattr(view, "openBreakdownLines", 
                                                   # for file output, 1 entry row if no facts
                                                   0 if filteredFactsPartitions else 1)):
                                view.aspectEntryObjectId += 1
                                filteredFactsPartitions.append([FactPrototype(view, {"aspectEntryObjectId": OPEN_ASPECT_ENTRY_SURROGATE + str(view.aspectEntryObjectId)})])
                                if structuralNode.isEntryPrototype(default=False):
                                    break # only one node per cartesian product under outermost nested open entry row
                    else:
                        childList = structuralNode.choiceStructuralNodes
                    for factsPartition in filteredFactsPartitions:
                        childStructuralNode = StructuralNode(structuralNode, breakdownNode, definitionNode, contextItemFact=factsPartition[0])
                        childStructuralNode.indent = 0
                        childStructuralNode.depth -= 1  # for label width; parent is merged/invisible
                        childList.append(childStructuralNode)
                        checkLabelWidth(childStructuralNode, checkBoundFact=True)
                        #expandDefinition(view, childStructuralNode, breakdownNode, definitionNode, depth, axisDisposition, factsPartition, processOpenDefinitionNode=False) #recurse
                        cartesianProductNestedArgs[3] = factsPartition
                        # note: reduced set of facts should always be passed to subsequent open nodes
                        if subtreeRelationships:
                            for axisSubtreeRel in subtreeRelationships:
                                child2DefinitionNode = axisSubtreeRel.toModelObject
                                child2StructuralNode = StructuralNode(childStructuralNode, breakdownNode, child2DefinitionNode) # others are nested structuralNode
                                childStructuralNode.childStructuralNodes.append(child2StructuralNode)
                                expandDefinition(view, child2StructuralNode, breakdownNode, child2DefinitionNode, depth+ordDepth, axisDisposition, factsPartition) #recurse
                                cartesianProductExpander(child2StructuralNode, *cartesianProductNestedArgs)
                        else:
                            cartesianProductExpander(childStructuralNode, *cartesianProductNestedArgs)
                    # sort by header (which is likely to be typed dim value, for example)
                    childList.sort(key=lambda childStructuralNode: 
                                   childStructuralNode.header(lang=view.lang, 
                                                              returnGenLabel=False, 
                                                              returnMsgFormatString=False) 
                                   or '') # exception on trying to sort if header returns None
                    
                    # TBD if there is no abstract 'sub header' for these subOrdCntxs, move them in place of parent structuralNode 
                elif isinstance(definitionNode, ModelTupleDefinitionNode):
                    structuralNode.abstract = True # spanning ordinate acts as a subtitle
                    matchingTupleFacts = structuralNode.evaluate(definitionNode, 
                                                                 definitionNode.filteredFacts, 
                                                                 evalArgs=(facts,))
                    for tupleFact in matchingTupleFacts:
                        childStructuralNode = StructuralNode(structuralNode, breakdownNode, definitionNode, contextItemFact=tupleFact)
                        childStructuralNode.indent = 0
                        structuralNode.childStructuralNodes.append(childStructuralNode)
                        expandDefinition(view, childStructuralNode, breakdownNode, definitionNode, depth, axisDisposition, [tupleFact]) #recurse
                    # sort by header (which is likely to be typed dim value, for example)
                    if (structuralNode.childStructuralNodes and
                        any(sOC.header(lang=view.lang) for sOC in structuralNode.childStructuralNodes)):
                        structuralNode.childStructuralNodes.sort(key=lambda childStructuralNode: childStructuralNode.header(lang=view.lang) or '')
                elif isinstance(definitionNode, ModelRuleDefinitionNode):
                    for constraintSet in definitionNode.constraintSets.values():
                        for aspect in constraintSet.aspectsCovered():
                            if not constraintSet.aspectValueDependsOnVars(aspect):
                                if aspect == Aspect.CONCEPT:
                                    conceptQname = definitionNode.aspectValue(view.rendrCntx, Aspect.CONCEPT)
                                    concept = view.modelXbrl.qnameConcepts.get(conceptQname)
                                    if concept is None or not concept.isItem or concept.isDimensionItem or concept.isHypercubeItem:
                                        view.modelXbrl.error("xbrlte:invalidQNameAspectValue",
                                            _("Rule node %(xlinkLabel)s specifies concept %(concept)s does not refer to an existing primary item concept."),
                                            modelObject=definitionNode, xlinkLabel=definitionNode.xlinkLabel, concept=conceptQname)
                                elif isinstance(aspect, QName):
                                    dim = view.modelXbrl.qnameConcepts.get(aspect)
                                    memQname = definitionNode.aspectValue(view.rendrCntx, aspect)
                                    mem = view.modelXbrl.qnameConcepts.get(memQname)
                                    if dim is None or not dim.isDimensionItem:
                                        view.modelXbrl.error("xbrlte:invalidQNameAspectValue",
                                            _("Rule node %(xlinkLabel)s specifies dimension %(concept)s does not refer to an existing dimension concept."),
                                            modelObject=definitionNode, xlinkLabel=definitionNode.xlinkLabel, concept=aspect)
                                    if isinstance(memQname, QName) and (mem is None or not mem.isDomainMember):
                                        view.modelXbrl.error("xbrlte:invalidQNameAspectValue",
                                            _("Rule node %(xlinkLabel)s specifies domain member %(concept)s does not refer to an existing domain member concept."),
                                            modelObject=definitionNode, xlinkLabel=definitionNode.xlinkLabel, concept=memQname)
    
                if axisDisposition == "z":
                    if structuralNode.choiceStructuralNodes:
                        choiceNodeIndex = view.zOrdinateChoices.get(definitionNode, 0)
                        if isinstance(choiceNodeIndex, dict):  # aspect entry for open node
                            structuralNode.aspects = choiceNodeIndex
                            structuralNode.choiceNodeIndex = -1
                        elif choiceNodeIndex < len(structuralNode.choiceStructuralNodes):
                            structuralNode.choiceNodeIndex = choiceNodeIndex
                        else:
                            structuralNode.choiceNodeIndex = 0
                    view.zmostOrdCntx = structuralNode
                        
                if not isCartesianProductExpanded or (axisDisposition == "z" and structuralNode.choiceStructuralNodes is not None):
                    cartesianProductExpander(structuralNode, *cartesianProductNestedArgs)
                        
                if not structuralNode.childStructuralNodes: # childless root ordinate, make a child to iterate in producing table
                    subOrdContext = StructuralNode(structuralNode, breakdownNode, definitionNode)
        except ResolutionException as ex:
            if sys.version[0] >= '3':
                #import traceback
                #traceback.print_tb(ex.__traceback__)
                raise ex.with_traceback(ex.__traceback__)  # provide original traceback information
            else:
                raise ex
        except Exception as ex:
            e = ResolutionException("arelle:resolutionException",
                                    _("Exception in resolution of definition node %(node)s: %(error)s"),
                                    modelObject=definitionNode, node=definitionNode.qname, error=str(ex)
                                    )
            if sys.version[0] >= '3':
                raise e.with_traceback(ex.__traceback__)  # provide original traceback information
            else:
                raise e
dataset/ETHPy150Open Arelle/Arelle/arelle/RenderingResolver.py/expandDefinition/original