Esempio n. 1
0
    def test_loop_repeat(self):
        initialCounts = {}
        counter = NodeCounter(initialCounts)
        counter.reset_to_node('/ISA_LOOP/GS_LOOP/ST_LOOP')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/ST')
        counter.reset_to_node('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/BPR')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/TRN')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/REF[EV]')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/DTM')
        counter.reset_to_node('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/1000A')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/1000A')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/1000A/N1')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/1000A/N3')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/1000A/N4')
        counter.reset_to_node('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/1000B')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/1000B')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/HEADER/1000B/N1')
        counter.reset_to_node('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/LX')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/TS3')
        counter.reset_to_node('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100')

        self.assertEqual(0, counter.get_count('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110'))
        counter.reset_to_node('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110')
        self.assertEqual(0, counter.get_count('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110'))
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110')
        self.assertEqual(1, counter.get_count('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110'))
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110/SVC')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110/DTM')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110/DTM')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110/CAS')
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110/LQ')

        self.assertEqual(1, counter.get_count('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110'))
        counter.reset_to_node('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110')
        self.assertEqual(1, counter.get_count('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110'))
        counter.increment('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110')
        self.assertEqual(2, counter.get_count('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100/2110'))
Esempio n. 2
0
class walk_tree(object):
    """
    Walks a map_if tree.  Tracks loop/segment counting, missing loop/segment.
    """
    def __init__(self, initialCounts=None):
        # Store errors until we know we have an error
        self.mandatory_segs_missing = []
        if initialCounts is None:
            initialCounts = {}
        self.counter = NodeCounter(initialCounts)

    def walk(self, node, seg_data, errh, seg_count, cur_line, ls_id):
        """
        Walk the node tree from the starting node to the node matching
        seg_data. Catch any counting or requirement errors along the way.

        Handle required segment/loop missed (not found in seg)
        Handle found segment = Not used

        @param node: Starting node
        @type node: L{node<map_if.x12_node>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param seg_count: Count of current segment in the ST Loop
        @type seg_count: int
        @param cur_line: Current line number in the file
        @type cur_line: int
        @param ls_id: The current LS loop identifier
        @type ls_id: string
        @return: The matching x12 segment node, a list of x12 popped loops, and a list
            of x12 pushed loops from the start segment to the found segment
        @rtype: (L{node<map_if.segment_if>}, [L{node<map_if.loop_if>}], [L{node<map_if.loop_if>}])

        @todo: check single segment loop repeat
        """
        pop_node_list = []
        push_node_list = []
        orig_node = node
        #logger.info('%s seg_count=%i / cur_line=%i' % (node.id, seg_count, cur_line))
        self.mandatory_segs_missing = []
        node_pos = node.pos  # Get original position ordinal of starting node
        if not (node.is_loop() or node.is_map_root()):
            node = pop_to_parent_loop(node)  # Get enclosing loop
            #node_list.append(node)
        while True:
            # Iterate through nodes with position >= current position
            for ord1 in [a for a in sorted(node.pos_map) if a >= node_pos]:
                for child in node.pos_map[ord1]:
                    if child.is_segment():
                        if child.is_match(seg_data):
                            # Is the matched segment the beginning of a loop?
                            if node.is_loop() \
                                    and self._is_loop_match(node, seg_data, errh, seg_count, cur_line, ls_id):
                                (
                                    node1, push_node_list) = self._goto_seg_match(node, seg_data,
                                                                                  errh, seg_count, cur_line, ls_id)
                                if orig_node.is_loop() or orig_node.is_map_root():
                                    orig_loop = orig_node
                                else:
                                    orig_loop = pop_to_parent_loop(orig_node)  # Get enclosing loop
                                if node == orig_loop:
                                    pop_node_list = [node]
                                    push_node_list = [node]
                                return (node1, pop_node_list, push_node_list)  # segment node
                            #child.incr_cur_count()
                            self.counter.increment(child.x12path)
                            #assert child.get_cur_count() == self.counter.get_count(child.x12path), \
                            #    'child counts not equal: old is %s=%i : new is %s=%i' % (
                            #    child.get_path(), child.get_cur_count(),
                            #    child.x12path.format(), self.counter.get_count(child.x12path))
                            self._check_seg_usage(child, seg_data, seg_count, cur_line, ls_id, errh)
                            # Remove any previously missing errors for this segment
                            self.mandatory_segs_missing = [x for x in self.mandatory_segs_missing if x[0] != child]
                            self._flush_mandatory_segs(errh, child.pos)
                            return (child, pop_node_list, push_node_list)  # segment node
                        elif child.usage == 'R' and self.counter.get_count(child.x12path) < 1:
                            fake_seg = pyx12.segment.Segment('%s' % (child.id), '~', '*', ':')
                            err_str = 'Mandatory segment "%s" (%s) missing' % (child.name, child.id)
                            self.mandatory_segs_missing.append((child, fake_seg, '3', err_str, seg_count, cur_line, ls_id))
                        #else:
                            #logger.debug('Segment %s is not a match for (%s*%s)' % \
                            #   (child.id, seg_data.get_seg_id(), seg_data[0].get_value()))
                    elif child.is_loop():
                        if self._is_loop_match(child, seg_data, errh, seg_count, cur_line, ls_id):
                            (node_seg, push_node_list) = self._goto_seg_match(child, seg_data, errh, seg_count, cur_line, ls_id)
                            return (node_seg, pop_node_list, push_node_list)  # segment node
            # End for ord1 in pos_keys
            if node.is_map_root():  # If at root and we haven't found the segment yet.
                walk_tree._seg_not_found_error(orig_node, seg_data,
                                               errh, seg_count, cur_line, ls_id)
                return (None, [], [])
            node_pos = node.pos  # Get position ordinal of current node in tree
            pop_node_list.append(node)
            node = pop_to_parent_loop(node)  # Get enclosing parent loop

        walk_tree._seg_not_found_error(orig_node, seg_data, errh, seg_count, cur_line, ls_id)
        return (None, [], [])

    def getCountState(self):
        return self.counter.getState()

    def setCountState(self, initialCounts={}):
        self.counter = NodeCounter(initialCounts)

    def forceWalkCounterToLoopStart(self, x12_path, child_path):
        # delete child counts under the x12_path, no longer needed
        self.counter.reset_to_node(x12_path)
        self.counter.increment(x12_path)  # add a count for this path
        self.counter.increment(child_path) # count the loop start segment

    def _check_seg_usage(self, seg_node, seg_data, seg_count, cur_line, ls_id, errh):
        """
        Check segment usage requirement and count

        @param seg_node: Segment X12 node to verify
        @type seg_node: L{node<map_if.segment_if>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param seg_count: Count of current segment in the ST Loop
        @type seg_count: int
        @param cur_line: Current line number in the file
        @type cur_line: int
        @param ls_id: The current LS loop identifier
        @type ls_id: string
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        @raise EngineError: On invalid usage code
        """
        assert seg_node.usage in ('N', 'R', 'S'), 'Segment usage must be R, S, or N'
        if seg_node.usage == 'N':
            err_str = "Segment %s found but marked as not used" % (seg_node.id)
            errh.seg_error('2', err_str, None)
        elif seg_node.usage == 'R' or seg_node.usage == 'S':
            #assert seg_node.get_cur_count() == self.counter.get_count(seg_node.x12path), 'seg_node counts not equal'
            if self.counter.get_count(seg_node.x12path) > seg_node.get_max_repeat():  # handle seg repeat count
                err_str = "Segment %s exceeded max count.  Found %i, should have %i" \
                    % (seg_data.get_seg_id(), self.counter.get_count(seg_node.x12path), seg_node.get_max_repeat())
                errh.add_seg(seg_node, seg_data, seg_count, cur_line, ls_id)
                errh.seg_error('5', err_str, None)

    @staticmethod
    def _seg_not_found_error(orig_node, seg_data, errh, seg_count, cur_line, ls_id):
        """
        Create error for not found segments

        @param orig_node: Original starting node
        @type orig_node: L{node<map_if.x12_node>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        """
        if seg_data.get_seg_id() == 'HL':
            seg_str = seg_data.format('', '*', ':')
        else:
            seg_str = '%s*%s' % (seg_data.get_seg_id(), seg_data.get_value('01'))
        err_str = 'Segment %s not found.  Started at %s' % (seg_str, orig_node.get_path())
        errh.add_seg(orig_node, seg_data, seg_count, cur_line, ls_id)
        errh.seg_error('1', err_str, None)

    def _flush_mandatory_segs(self, errh, cur_pos=None):
        """
        Handle error reporting for any outstanding missing mandatory segments

        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        """
        for (seg_node, seg_data, err_cde, err_str, seg_count, cur_line, ls_id) in self.mandatory_segs_missing:
            # Create errors if not also at current position
            if seg_node.pos != cur_pos:
                errh.add_seg(seg_node, seg_data, seg_count, cur_line, ls_id)
                errh.seg_error(err_cde, err_str, None)
        self.mandatory_segs_missing = [x for x in self.mandatory_segs_missing if x[0].pos == cur_pos]

    def _is_loop_match(self, loop_node, seg_data, errh, seg_count, cur_line, ls_id):
        """
        Try to match the current loop to the segment
        Handle loop and segment counting.
        Check for used/missing

        @param loop_node: Loop Node
        @type loop_node: L{node<map_if.loop_if>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}

        @return: Does the segment match the first segment node in the loop?
        @rtype: boolean
        """
        assert loop_node.is_loop(), "Call to first_seg_match failed, node %s is not a loop. seg %s" \
            % (loop_node.id, seg_data.get_seg_id())
        #if loop_node.id not in ('ISA_LOOP', 'GS_LOOP'):
        #    assert loop_node.get_cur_count() == self.counter.get_count(loop_node.x12path), \
        #        'loop_node counts not equal: old is %s=%i : new is %s=%i' % (
        #        loop_node.get_path(), loop_node.get_cur_count(),
        #        loop_node.x12path.format(), self.counter.get_count(loop_node.x12path))
        if len(loop_node) <= 0:  # Has no children
            return False
        first_child_node = loop_node.get_first_node()
        assert first_child_node is not None, 'get_first_node failed from loop %s' % (loop_node.id)
        if first_child_node.is_loop():
            #If any loop node matches
            for child_node in loop_node.childIterator():
                if child_node.is_loop() and self._is_loop_match(child_node,
                                                                seg_data, errh, seg_count, cur_line, ls_id):
                    return True
        elif is_first_seg_match2(first_child_node, seg_data):
            return True
        elif loop_node.usage == 'R' and self.counter.get_count(loop_node.x12path) < 1:
            fake_seg = pyx12.segment.Segment('%s' % (first_child_node.id), '~', '*', ':')
            err_str = 'Mandatory loop "%s" (%s) missing' % \
                (loop_node.name, loop_node.id)
            self.mandatory_segs_missing.append((first_child_node, fake_seg,
                                                '3', err_str, seg_count, cur_line, ls_id))
        return False

    def _goto_seg_match(self, loop_node, seg_data, errh, seg_count, cur_line, ls_id):
        """
        A child loop has matched the segment.  Return that segment node.
        Handle loop counting and requirement errors.

        @param loop_node: The starting loop node.
        @type loop_node: L{node<map_if.loop_if>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        @param seg_count: Current segment count for ST loop
        @type seg_count: int
        @param cur_line: File line counter
        @type cur_line: int
        @type ls_id: string

        @return: The matching segment node and a list of the push loop nodes
        @rtype: (L{node<map_if.segment_if>}, [L{node<map_if.loop_if>}])
        """
        assert loop_node.is_loop(), "_goto_seg_match failed, node %s is not a loop. seg %s" \
            % (loop_node.id, seg_data.get_seg_id())
        first_child_node = loop_node.get_first_seg()
        if first_child_node is not None and is_first_seg_match2(first_child_node, seg_data):
            self._check_loop_usage(loop_node, seg_data,
                                   seg_count, cur_line, ls_id, errh)
            #first_child_node.incr_cur_count()
            self.counter.increment(first_child_node.x12path)
            #assert first_child_node.get_cur_count() == self.counter.get_count(first_child_node.x12path), 'first_child_node counts not equal'
            self._flush_mandatory_segs(errh)
            return (first_child_node, [loop_node])
        else:
            for child in loop_node.childIterator():
                if child.is_loop():
                    (
                        node1, push1) = self._goto_seg_match(child, seg_data, errh,
                                                             seg_count, cur_line, ls_id)
                    if node1:
                        push_node_list = [loop_node]
                        push_node_list.extend(push1)
                        return (node1, push_node_list)
        return (None, [])

    def _check_loop_usage(self, loop_node, seg_data, seg_count, cur_line, ls_id, errh):
        """
        Check loop usage requirement and count

        @param loop_node: Loop X12 node to verify
        @type loop_node: L{node<map_if.loop_if>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param seg_count: Count of current segment in the ST Loop
        @type seg_count: int
        @param cur_line: Current line number in the file
        @type cur_line: int
        @param ls_id: The current LS loop identifier
        @type ls_id: string
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        @raise EngineError: On invalid usage code
        """
        assert loop_node.is_loop(), "Node %s is not a loop. seg %s" % (
            loop_node.id, seg_data.get_seg_id())
        assert loop_node.usage in ('N', 'R', 'S'), 'Loop usage must be R, S, or N'
        if loop_node.usage == 'N':
            err_str = "Loop %s found but marked as not used" % (loop_node.id)
            errh.seg_error('2', err_str, None)
        elif loop_node.usage in ('R', 'S'):
            #if loop_node.id == '2110':
            #    import ipdb; ipdb.set_trace()
            #loop_node.reset_child_count()
            self.counter.reset_to_node(loop_node.x12path)
            #loop_node.incr_cur_count()
            self.counter.increment(loop_node.x12path)
            #assert loop_node.get_cur_count() == self.counter.get_count(loop_node.x12path), \
            #    'loop_node counts not equal: old is %s=%i : new is %s=%i' % (
            #    loop_node.get_path(), loop_node.get_cur_count(),
            #    loop_node.x12path.format(), self.counter.get_count(loop_node.x12path))
            #logger.debug('incr loop_node %s %i' % (loop_node.id, loop_node.cur_count))
            #logger.debug('incr first_child_node %s %i' % (first_child_node.id, first_child_node.cur_count))
            if self.counter.get_count(loop_node.x12path) > loop_node.get_max_repeat():
                err_str = "Loop %s exceeded max count.  Found %i, should have %i" \
                    % (loop_node.id, self.counter.get_count(loop_node.x12path), loop_node.get_max_repeat())
                errh.add_seg(loop_node, seg_data, seg_count, cur_line, ls_id)
                errh.seg_error('4', err_str, None)
Esempio n. 3
0
class walk_tree(object):
    """
    Walks a map_if tree.  Tracks loop/segment counting, missing loop/segment.
    """
    def __init__(self, initialCounts=None):
        # Store errors until we know we have an error
        self.mandatory_segs_missing = []
        if initialCounts is None:
            initialCounts = {}
        self.counter = NodeCounter(initialCounts)

    def walk(self, node, seg_data, errh, seg_count, cur_line, ls_id):
        """
        Walk the node tree from the starting node to the node matching
        seg_data. Catch any counting or requirement errors along the way.

        Handle required segment/loop missed (not found in seg)
        Handle found segment = Not used

        @param node: Starting node
        @type node: L{node<map_if.x12_node>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param seg_count: Count of current segment in the ST Loop
        @type seg_count: int
        @param cur_line: Current line number in the file
        @type cur_line: int
        @param ls_id: The current LS loop identifier
        @type ls_id: string
        @return: The matching x12 segment node, a list of x12 popped loops, and a list
            of x12 pushed loops from the start segment to the found segment
        @rtype: (L{node<map_if.segment_if>}, [L{node<map_if.loop_if>}], [L{node<map_if.loop_if>}])

        @todo: check single segment loop repeat
        """
        pop_node_list = []
        push_node_list = []
        orig_node = node
        #logger.info('%s seg_count=%i / cur_line=%i' % (node.id, seg_count, cur_line))
        self.mandatory_segs_missing = []
        node_pos = node.pos  # Get original position ordinal of starting node
        if not (node.is_loop() or node.is_map_root()):
            node = pop_to_parent_loop(node)  # Get enclosing loop
            #node_list.append(node)
        while True:
            # Iterate through nodes with position >= current position
            for ord1 in [a for a in sorted(node.pos_map) if a >= node_pos]:
                for child in node.pos_map[ord1]:
                    if child.is_segment():
                        if child.is_match(seg_data):
                            # Is the matched segment the beginning of a loop?
                            if node.is_loop() \
                                    and self._is_loop_match(node, seg_data, errh, seg_count, cur_line, ls_id):
                                (node1, push_node_list) = self._goto_seg_match(
                                    node, seg_data, errh, seg_count, cur_line,
                                    ls_id)
                                if orig_node.is_loop(
                                ) or orig_node.is_map_root():
                                    orig_loop = orig_node
                                else:
                                    orig_loop = pop_to_parent_loop(
                                        orig_node)  # Get enclosing loop
                                if node == orig_loop:
                                    pop_node_list = [node]
                                    push_node_list = [node]
                                return (node1, pop_node_list, push_node_list
                                        )  # segment node
                            #child.incr_cur_count()
                            self.counter.increment(child.x12path)
                            #assert child.get_cur_count() == self.counter.get_count(child.x12path), \
                            #    'child counts not equal: old is %s=%i : new is %s=%i' % (
                            #    child.get_path(), child.get_cur_count(),
                            #    child.x12path.format(), self.counter.get_count(child.x12path))
                            self._check_seg_usage(child, seg_data, seg_count,
                                                  cur_line, ls_id, errh)
                            # Remove any previously missing errors for this segment
                            self.mandatory_segs_missing = [
                                x for x in self.mandatory_segs_missing
                                if x[0] != child
                            ]
                            self._flush_mandatory_segs(errh, child.pos)
                            return (child, pop_node_list, push_node_list
                                    )  # segment node
                        elif child.usage == 'R' and self.counter.get_count(
                                child.x12path) < 1:
                            fake_seg = pyx12.segment.Segment(
                                '%s' % (child.id), '~', '*', ':')
                            err_str = 'Mandatory segment "%s" (%s) missing' % (
                                child.name, child.id)
                            self.mandatory_segs_missing.append(
                                (child, fake_seg, '3', err_str, seg_count,
                                 cur_line, ls_id))
                        #else:
                        #logger.debug('Segment %s is not a match for (%s*%s)' % \
                        #   (child.id, seg_data.get_seg_id(), seg_data[0].get_value()))
                    elif child.is_loop():
                        if self._is_loop_match(child, seg_data, errh,
                                               seg_count, cur_line, ls_id):
                            (node_seg, push_node_list) = self._goto_seg_match(
                                child, seg_data, errh, seg_count, cur_line,
                                ls_id)
                            return (node_seg, pop_node_list, push_node_list
                                    )  # segment node
            # End for ord1 in pos_keys
            if node.is_map_root(
            ):  # If at root and we haven't found the segment yet.
                walk_tree._seg_not_found_error(orig_node, seg_data, errh,
                                               seg_count, cur_line, ls_id)
                return (None, [], [])
            node_pos = node.pos  # Get position ordinal of current node in tree
            pop_node_list.append(node)
            node = pop_to_parent_loop(node)  # Get enclosing parent loop

        walk_tree._seg_not_found_error(orig_node, seg_data, errh, seg_count,
                                       cur_line, ls_id)
        return (None, [], [])

    def getCountState(self):
        return self.counter.getState()

    def setCountState(self, initialCounts={}):
        self.counter = NodeCounter(initialCounts)

    def forceWalkCounterToLoopStart(self, x12_path, child_path):
        # delete child counts under the x12_path, no longer needed
        self.counter.reset_to_node(x12_path)
        self.counter.increment(x12_path)  # add a count for this path
        self.counter.increment(child_path)  # count the loop start segment

    def _check_seg_usage(self, seg_node, seg_data, seg_count, cur_line, ls_id,
                         errh):
        """
        Check segment usage requirement and count

        @param seg_node: Segment X12 node to verify
        @type seg_node: L{node<map_if.segment_if>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param seg_count: Count of current segment in the ST Loop
        @type seg_count: int
        @param cur_line: Current line number in the file
        @type cur_line: int
        @param ls_id: The current LS loop identifier
        @type ls_id: string
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        @raise EngineError: On invalid usage code
        """
        assert seg_node.usage in ('N', 'R',
                                  'S'), 'Segment usage must be R, S, or N'
        if seg_node.usage == 'N':
            err_str = "Segment %s found but marked as not used" % (seg_node.id)
            errh.seg_error('2', err_str, None)
        elif seg_node.usage == 'R' or seg_node.usage == 'S':
            #assert seg_node.get_cur_count() == self.counter.get_count(seg_node.x12path), 'seg_node counts not equal'
            if self.counter.get_count(
                    seg_node.x12path) > seg_node.get_max_repeat(
                    ):  # handle seg repeat count
                err_str = "Segment %s exceeded max count.  Found %i, should have %i" \
                    % (seg_data.get_seg_id(), self.counter.get_count(seg_node.x12path), seg_node.get_max_repeat())
                errh.add_seg(seg_node, seg_data, seg_count, cur_line, ls_id)
                errh.seg_error('5', err_str, None)

    @staticmethod
    def _seg_not_found_error(orig_node, seg_data, errh, seg_count, cur_line,
                             ls_id):
        """
        Create error for not found segments

        @param orig_node: Original starting node
        @type orig_node: L{node<map_if.x12_node>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        """
        if seg_data.get_seg_id() == 'HL':
            seg_str = seg_data.format('', '*', ':')
        else:
            seg_str = '%s*%s' % (seg_data.get_seg_id(),
                                 seg_data.get_value('01'))
        err_str = 'Segment %s not found.  Started at %s' % (
            seg_str, orig_node.get_path())
        errh.add_seg(orig_node, seg_data, seg_count, cur_line, ls_id)
        errh.seg_error('1', err_str, None)

    def _flush_mandatory_segs(self, errh, cur_pos=None):
        """
        Handle error reporting for any outstanding missing mandatory segments

        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        """
        for (seg_node, seg_data, err_cde, err_str, seg_count, cur_line,
             ls_id) in self.mandatory_segs_missing:
            # Create errors if not also at current position
            if seg_node.pos != cur_pos:
                errh.add_seg(seg_node, seg_data, seg_count, cur_line, ls_id)
                errh.seg_error(err_cde, err_str, None)
        self.mandatory_segs_missing = [
            x for x in self.mandatory_segs_missing if x[0].pos == cur_pos
        ]

    def _is_loop_match(self, loop_node, seg_data, errh, seg_count, cur_line,
                       ls_id):
        """
        Try to match the current loop to the segment
        Handle loop and segment counting.
        Check for used/missing

        @param loop_node: Loop Node
        @type loop_node: L{node<map_if.loop_if>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}

        @return: Does the segment match the first segment node in the loop?
        @rtype: boolean
        """
        assert loop_node.is_loop(), "Call to first_seg_match failed, node %s is not a loop. seg %s" \
            % (loop_node.id, seg_data.get_seg_id())
        #if loop_node.id not in ('ISA_LOOP', 'GS_LOOP'):
        #    assert loop_node.get_cur_count() == self.counter.get_count(loop_node.x12path), \
        #        'loop_node counts not equal: old is %s=%i : new is %s=%i' % (
        #        loop_node.get_path(), loop_node.get_cur_count(),
        #        loop_node.x12path.format(), self.counter.get_count(loop_node.x12path))
        if len(loop_node) <= 0:  # Has no children
            return False
        first_child_node = loop_node.get_first_node()
        assert first_child_node is not None, 'get_first_node failed from loop %s' % (
            loop_node.id)
        if first_child_node.is_loop():
            #If any loop node matches
            for child_node in loop_node.childIterator():
                if child_node.is_loop() and self._is_loop_match(
                        child_node, seg_data, errh, seg_count, cur_line,
                        ls_id):
                    return True
        elif is_first_seg_match2(first_child_node, seg_data):
            return True
        elif loop_node.usage == 'R' and self.counter.get_count(
                loop_node.x12path) < 1:
            fake_seg = pyx12.segment.Segment('%s' % (first_child_node.id), '~',
                                             '*', ':')
            err_str = 'Mandatory loop "%s" (%s) missing' % \
                (loop_node.name, loop_node.id)
            self.mandatory_segs_missing.append(
                (first_child_node, fake_seg, '3', err_str, seg_count, cur_line,
                 ls_id))
        return False

    def _goto_seg_match(self, loop_node, seg_data, errh, seg_count, cur_line,
                        ls_id):
        """
        A child loop has matched the segment.  Return that segment node.
        Handle loop counting and requirement errors.

        @param loop_node: The starting loop node.
        @type loop_node: L{node<map_if.loop_if>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        @param seg_count: Current segment count for ST loop
        @type seg_count: int
        @param cur_line: File line counter
        @type cur_line: int
        @type ls_id: string

        @return: The matching segment node and a list of the push loop nodes
        @rtype: (L{node<map_if.segment_if>}, [L{node<map_if.loop_if>}])
        """
        assert loop_node.is_loop(), "_goto_seg_match failed, node %s is not a loop. seg %s" \
            % (loop_node.id, seg_data.get_seg_id())
        first_child_node = loop_node.get_first_seg()
        if first_child_node is not None and is_first_seg_match2(
                first_child_node, seg_data):
            self._check_loop_usage(loop_node, seg_data, seg_count, cur_line,
                                   ls_id, errh)
            #first_child_node.incr_cur_count()
            self.counter.increment(first_child_node.x12path)
            #assert first_child_node.get_cur_count() == self.counter.get_count(first_child_node.x12path), 'first_child_node counts not equal'
            self._flush_mandatory_segs(errh)
            return (first_child_node, [loop_node])
        else:
            for child in loop_node.childIterator():
                if child.is_loop():
                    (node1,
                     push1) = self._goto_seg_match(child, seg_data, errh,
                                                   seg_count, cur_line, ls_id)
                    if node1:
                        push_node_list = [loop_node]
                        push_node_list.extend(push1)
                        return (node1, push_node_list)
        return (None, [])

    def _check_loop_usage(self, loop_node, seg_data, seg_count, cur_line,
                          ls_id, errh):
        """
        Check loop usage requirement and count

        @param loop_node: Loop X12 node to verify
        @type loop_node: L{node<map_if.loop_if>}
        @param seg_data: Segment object
        @type seg_data: L{segment<segment.Segment>}
        @param seg_count: Count of current segment in the ST Loop
        @type seg_count: int
        @param cur_line: Current line number in the file
        @type cur_line: int
        @param ls_id: The current LS loop identifier
        @type ls_id: string
        @param errh: Error handler
        @type errh: L{error_handler.err_handler}
        @raise EngineError: On invalid usage code
        """
        assert loop_node.is_loop(), "Node %s is not a loop. seg %s" % (
            loop_node.id, seg_data.get_seg_id())
        assert loop_node.usage in ('N', 'R',
                                   'S'), 'Loop usage must be R, S, or N'
        if loop_node.usage == 'N':
            err_str = "Loop %s found but marked as not used" % (loop_node.id)
            errh.seg_error('2', err_str, None)
        elif loop_node.usage in ('R', 'S'):
            #if loop_node.id == '2110':
            #    import ipdb; ipdb.set_trace()
            #loop_node.reset_child_count()
            self.counter.reset_to_node(loop_node.x12path)
            #loop_node.incr_cur_count()
            self.counter.increment(loop_node.x12path)
            #assert loop_node.get_cur_count() == self.counter.get_count(loop_node.x12path), \
            #    'loop_node counts not equal: old is %s=%i : new is %s=%i' % (
            #    loop_node.get_path(), loop_node.get_cur_count(),
            #    loop_node.x12path.format(), self.counter.get_count(loop_node.x12path))
            #logger.debug('incr loop_node %s %i' % (loop_node.id, loop_node.cur_count))
            #logger.debug('incr first_child_node %s %i' % (first_child_node.id, first_child_node.cur_count))
            if self.counter.get_count(
                    loop_node.x12path) > loop_node.get_max_repeat():
                err_str = "Loop %s exceeded max count.  Found %i, should have %i" \
                    % (loop_node.id, self.counter.get_count(loop_node.x12path), loop_node.get_max_repeat())
                errh.add_seg(loop_node, seg_data, seg_count, cur_line, ls_id)
                errh.seg_error('4', err_str, None)