1
2
3
4
5
6
7
8
9
10
11
12
13 """
14 Walk a tree of x12_map nodes. Find the correct node.
15
16 If seg indicates a loop has been entered, returns the first child segment node.
17 If seg indicates a segment has been entered, returns the segment node.
18 """
19
20 import logging
21 import pdb
22
23
24 from errors import *
25 import pyx12.segment
26
27 logger = logging.getLogger('pyx12.walk_tree')
28
29
30
32 """
33 @param node: Loop Node
34 @type node: L{node<map_if.x12_node>}
35 @return: Closest parent loop node
36 @rtype: L{node<map_if.x12_node>}
37 """
38 if node.is_map_root():
39 return node
40 map_node = node.parent
41 if map_node is None:
42 raise EngineError, "Node is None: %s" % (node.name)
43 while not (map_node.is_loop() or map_node.is_map_root()):
44 map_node = map_node.parent
45 if not (map_node.is_loop() or map_node.is_map_root()):
46 raise EngineError, "Called pop_to_parent_loop, can't find parent loop"
47 return map_node
48
50 """
51 Find the first segment in loop, verify it matches segment
52
53 @param child: child node
54 @type child: L{node<map_if.x12_node>}
55 @param seg_data: Segment object
56 @type seg_data: L{segment<segment.Segment>}
57 @rtype: boolean
58 """
59 if child.is_segment():
60 if child.is_match(seg_data):
61 return True
62 else:
63 return False
64 return False
65
66
68 """
69 Walks a map_if tree. Tracks loop/segment counting, missing loop/segment.
70 """
72
73
74 self.mandatory_segs_missing = []
75 pass
76
77 - def walk(self, node, seg_data, errh, seg_count, cur_line, ls_id):
78 """
79 Walk the node tree from the starting node to the node matching
80 seg_data. Catch any counting or requirement errors along the way.
81
82 Handle required segment/loop missed (not found in seg)
83 Handle found segment = Not used
84
85 @param node: Starting node
86 @type node: L{node<map_if.x12_node>}
87 @param seg_data: Segment object
88 @type seg_data: L{segment<segment.Segment>}
89 @param seg_count: Count of current segment in the ST Loop
90 @type seg_count: int
91 @param cur_line: Current line number in the file
92 @type cur_line: int
93 @param ls_id: The current LS loop identifier
94 @type ls_id: string
95 @return: The matching x12_node
96 @rtype: L{node<map_if.x12_node>}
97
98 @todo: check single segment loop repeat
99 """
100
101
102
103
104 orig_node = node
105
106 self.mandatory_segs_missing = []
107 node_pos = node.pos
108 if not (node.is_loop() or node.is_map_root()):
109 node = pop_to_parent_loop(node)
110 while True:
111
112
113 pos_keys = filter(lambda a: a>= node_pos, node.pos_map.keys())
114 pos_keys.sort()
115 for ord1 in pos_keys:
116
117
118
119 for child in node.pos_map[ord1]:
120
121 if child.is_segment():
122
123
124 if child.is_match(seg_data):
125
126 if node.is_loop() \
127 and self._is_loop_match(node, seg_data, errh, seg_count, cur_line, ls_id):
128 node1 = self._goto_seg_match(node, seg_data, \
129 errh, seg_count, cur_line, ls_id)
130 return node1
131
132 child.incr_cur_count()
133
134
135 if child.usage == 'N':
136 err_str = "Segment %s found but marked as not used" % (child.id)
137 errh.seg_error('2', err_str, None)
138 elif child.usage == 'R' or child.usage == 'S':
139 if child.get_cur_count() > child.get_max_repeat():
140 err_str = "Segment %s exceeded max count. Found %i, should have %i" \
141 % (seg_data.get_seg_id(), child.get_cur_count(), child.get_max_repeat())
142 errh.add_seg(child, seg_data, seg_count, cur_line, ls_id)
143 errh.seg_error('5', err_str, None)
144 else:
145 raise EngineError, 'Usage must be R, S, or N'
146
147 self.mandatory_segs_missing = filter(lambda x: x[0]!=child,
148 self.mandatory_segs_missing)
149 self._flush_mandatory_segs(errh, child.pos)
150 return child
151 elif child.usage == 'R' and child.get_cur_count() < 1:
152 fake_seg = pyx12.segment.Segment('%s'% (child.id), '~', '*', ':')
153
154 err_str = 'Mandatory segment "%s" (%s) missing' % (child.name, child.id)
155 self.mandatory_segs_missing.append((child, fake_seg,
156 '3', err_str, seg_count, cur_line, ls_id))
157
158
159
160 elif child.is_loop():
161
162 if self._is_loop_match(child, seg_data, errh, \
163 seg_count, cur_line, ls_id):
164 node_seg = self._goto_seg_match(child, seg_data, \
165 errh, seg_count, cur_line, ls_id)
166
167 return node_seg
168 if node.is_map_root():
169 self._seg_not_found(orig_node, seg_data, errh, seg_count, \
170 cur_line, ls_id)
171 return None
172 node_pos = node.pos
173 node = pop_to_parent_loop(node)
174
175 self._seg_not_found(orig_node, seg_data, errh, seg_count, cur_line, ls_id)
176 return None
177
178
179
180
181
182
183 - def _seg_not_found(self, orig_node, seg_data, errh, seg_count, cur_line, ls_id):
184 """
185 Create error for not found segments
186
187 @param orig_node: Original starting node
188 @type orig_node: L{node<map_if.x12_node>}
189 @param seg_data: Segment object
190 @type seg_data: L{segment<segment.Segment>}
191 @param errh: Error handler
192 @type errh: L{error_handler.err_handler}
193 """
194 if seg_data.get_seg_id() == 'HL':
195 seg_str = seg_data.format('', '*', ':')
196 else:
197 seg_str = '%s*%s' % (seg_data.get_seg_id(), seg_data.get_value('01'))
198 err_str = 'Segment %s not found. Started at %s' % (seg_str, orig_node.get_path())
199 errh.add_seg(orig_node, seg_data, seg_count, cur_line, ls_id)
200 errh.seg_error('1', err_str, None)
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
220 """
221 Handle error reporting for any outstanding missing mandatory segments
222
223 @param errh: Error handler
224 @type errh: L{error_handler.err_handler}
225 """
226
227 for (seg_node, seg_data, err_cde, err_str,
228 seg_count, cur_line, ls_id) in self.mandatory_segs_missing:
229
230 if seg_node.pos != cur_pos:
231 errh.add_seg(seg_node, seg_data, seg_count, cur_line, ls_id)
232 errh.seg_error(err_cde, err_str, None)
233 self.mandatory_segs_missing = filter(lambda x: x[0].pos==cur_pos,
234 self.mandatory_segs_missing)
235
236
237
238
239
240
241
242 - def _is_loop_match(self, loop_node, seg_data, errh, seg_count, cur_line, ls_id):
243 """
244 Try to match the current loop to the segment
245 Handle loop and segment counting.
246 Check for used/missing
247
248 @param loop_node: Loop Node
249 @type loop_node: L{node<map_if.loop_if>}
250 @param seg_data: Segment object
251 @type seg_data: L{segment<segment.Segment>}
252 @param errh: Error handler
253 @type errh: L{error_handler.err_handler}
254
255 @return: Does the segment match the first segment node in the loop?
256 @rtype: boolean
257 """
258
259
260 if not loop_node.is_loop(): raise EngineError, \
261 "Call to first_seg_match failed, node %s is not a loop. seg %s" \
262 % (loop_node.id, seg_data.get_seg_id())
263 if len(loop_node) <= 0:
264 return False
265
266 pos_keys = loop_node.pos_map.keys()
267 pos_keys.sort()
268
269 first_child_node = loop_node.pos_map[pos_keys[0]][0]
270 if first_child_node.is_loop():
271
272 for ord1 in pos_keys:
273 for child_node in loop_node.pos_map[ord1]:
274 if child_node.is_loop() and self._is_loop_match(child_node, \
275 seg_data, errh, seg_count, cur_line, ls_id):
276 return True
277 elif is_first_seg_match2(first_child_node, seg_data):
278 return True
279 elif loop_node.usage == 'R' and loop_node.get_cur_count() < 1:
280
281 fake_seg = pyx12.segment.Segment('%s' % \
282 (first_child_node.id), '~', '*', ':')
283
284 err_str = 'Mandatory loop "%s" (%s) missing' % \
285 (loop_node.name, loop_node.id)
286 self.mandatory_segs_missing.append((first_child_node, fake_seg, \
287 '3', err_str, seg_count, cur_line, ls_id))
288
289 return False
290
291 - def _goto_seg_match(self, loop_node, seg_data, errh, seg_count, cur_line, ls_id):
292 """
293 A child loop has matched the segment. Return that segment node.
294 Handle loop counting and requirement errors.
295
296 @param loop_node: The starting loop node.
297 @type loop_node: L{node<map_if.loop_if>}
298 @param seg_data: Segment object
299 @type seg_data: L{segment<segment.Segment>}
300 @param errh: Error handler
301 @type errh: L{error_handler.err_handler}
302 @param seg_count: Current segment count for ST loop
303 @type seg_count: int
304 @param cur_line: File line counter
305 @type cur_line: int
306 @type ls_id: string
307
308 @return: The matching segment node
309 @rtype: L{node<map_if.segment_if>}
310 """
311 if not loop_node.is_loop(): raise EngineError, \
312 "_goto_seg_match failed, node %s is not a loop. seg %s" \
313 % (loop_node.id, seg_data.get_seg_id())
314
315 pos_keys = loop_node.pos_map.keys()
316 pos_keys.sort()
317 first_child_node = loop_node.pos_map[pos_keys[0]][0]
318 if is_first_seg_match2(first_child_node, seg_data):
319 if loop_node.usage == 'N':
320 err_str = "Loop %s found but marked as not used" % (loop_node.id)
321 errh.seg_error('2', err_str, None)
322 elif loop_node.usage in ('R', 'S'):
323 loop_node.reset_child_count()
324 loop_node.incr_cur_count()
325
326 first_child_node.incr_cur_count()
327
328 if loop_node.get_cur_count() > loop_node.get_max_repeat():
329 err_str = "Loop %s exceeded max count. Found %i, should have %i" \
330 % (loop_node.id, loop_node.get_cur_count(), loop_node.get_max_repeat())
331 errh.add_seg(loop_node, seg_data, seg_count, cur_line, ls_id)
332 errh.seg_error('4', err_str, None)
333
334
335 else:
336 raise EngineError, 'Usage must be R, S, or N'
337 self._flush_mandatory_segs(errh)
338 return first_child_node
339 else:
340
341 for ord1 in pos_keys:
342 for child in loop_node.pos_map[ord1]:
343 if child.is_loop():
344 node1 = self._goto_seg_match(child, seg_data, errh, \
345 seg_count, cur_line, ls_id)
346 if node1:
347 return node1
348 return None
349