File Coverage

lib/Net/HL7/Message.pm
Criterion Covered Total %
statement 151 158 95.5
branch 48 56 85.7
condition 12 18 66.6
subroutine 19 20 95.0
pod 12 12 100.0
total 242 264 91.6


line stmt bran cond sub pod time code
1             package Net::HL7::Message;
2              
3 7     7   1817 use 5.004;
  7         20  
4 7     7   30 use strict;
  7         11  
  7         163  
5 7     7   25 use warnings;
  7         10  
  7         141  
6 7     7   1384 use Net::HL7::Segment;
  7         13  
  7         156  
7 7     7   1680 use Net::HL7;
  7         14  
  7         7599  
8              
9             =pod
10              
11             =head1 NAME
12              
13             Net::HL7::Message
14              
15             =head1 SYNOPSIS
16              
17             my $request = new Net::HL7::Request();
18             my $conn = new Net::HL7::Connection('localhost', 8089);
19              
20             my $msh = new Net::HL7::Segments::MSH();
21              
22             my $seg1 = new Net::HL7::Segment("PID");
23              
24             $seg1->setField(1, "foo");
25              
26             $request->addSegment($msh);
27             $request->addSegment($seg1);
28              
29             my $response = $conn->send($request);
30              
31              
32             =head1 DESCRIPTION
33              
34             In general one needn't create an instance of the Net::HL7::Message
35             class directly, but use the L
36             class. When adding segments, note that the segment index starts at 0,
37             so to get the first segment, segment, do
38             C<$msg-EgetSegmentByIndex(0)>.
39              
40             The segment separator defaults to \015. To change this, set the
41             variable $Net::HL7::SEGMENT_SEPARATOR.
42              
43              
44             =head1 METHODS
45              
46             =over 4
47              
48             =item B<$msg = new Net::HL7::Message([$msg])>
49              
50             The constructor takes an optional string argument that is a string
51             representation of a HL7 message. If the string representation is not a
52             valid HL7 message. according to the specifications, undef is returned
53             instead of a new instance. This means that segments should be
54             separated within the message with the segment separator (defaults to
55             \015) or a newline, and segments should be syntactically correct.
56             When using the string argument constructor, make sure that you have
57             escaped any characters that would have special meaning in Perl. For
58             instance (using a different subcomponent separator):
59              
60             C<$msg = new Net::HL7::Message("MSH*^~\\@*1\rPID***x^x@y@z^z\r");>
61              
62             would actually mean
63              
64             C<$msg = new Net::HL7::Message("MSH*^~\\@*1\rPID***x^x^z\r");>
65              
66             since '@y@z' would be interpreted as two empty arrays, so do:
67              
68             C<$msg = new Net::HL7::Message("MSH*^~\\@*1\rPID***x^x\@y\@z^z\r");>
69              
70             instead.
71              
72             The control characters and field separator will take the values from
73             the MSH segment, if set. Otherwise defaults will be used. Changing the
74             MSH fields specifying the field separator and control characters after
75             the MSH has been added to the message will result in setting these
76             values for the message.
77              
78             If the message couldn't be created, for example due to a erroneous HL7
79             message string, undef is returned.
80              
81             =cut
82              
83             sub new {
84              
85 23     23 1 2763 my $class = shift;
86 23         44 bless my $self = {}, $class;
87              
88 23 100       67 $self->_init(@_) || return undef;
89              
90 21         43 return $self;
91             }
92              
93              
94             sub _init {
95              
96 23     23   43 my ($self, $hl7str) = @_;
97              
98             # Array holding the segments
99             #
100 23         64 $self->{SEGMENTS} = [];
101              
102             # Control characters and other HL7 properties
103             #
104 23         42 $self->{SEGMENT_SEPARATOR} = $Net::HL7::SEGMENT_SEPARATOR;
105 23         50 $self->{FIELD_SEPARATOR} = $Net::HL7::FIELD_SEPARATOR;
106 23         56 $self->{COMPONENT_SEPARATOR} = $Net::HL7::COMPONENT_SEPARATOR;
107 23         32 $self->{SUBCOMPONENT_SEPARATOR} = $Net::HL7::SUBCOMPONENT_SEPARATOR;
108 23         36 $self->{REPETITION_SEPARATOR} = $Net::HL7::REPETITION_SEPARATOR;
109 23         29 $self->{ESCAPE_CHARACTER} = $Net::HL7::ESCAPE_CHARACTER;
110 23         52 $self->{HL7_VERSION} = $Net::HL7::HL7_VERSION;
111              
112             # If an HL7 string is given to the constructor, parse it.
113 23 100       57 if ($hl7str) {
114              
115 11         170 my @segments = split("[\n\\" . $self->{SEGMENT_SEPARATOR} . "]",
116             $hl7str);
117              
118             # the first segment should be the control segment
119             #
120 11         46 $segments[0] =~ /^([A-Z0-9]{3})(.)(.)(.)(.)(.)(.)/;
121              
122 11         67 my ($hdr, $fldSep, $compSep, $repSep, $esc, $subCompSep, $fldSepCtrl) =
123             ($1, $2, $3, $4, $5, $6, $7);
124              
125             # check for MSH
126 11 50 33     70 if( not defined $hdr or $hdr ne 'MSH' ) {
127 0         0 return undef;
128             }
129              
130             # Check whether field separator is repeated after 4 control characters
131              
132 11 100 33     74 if (not defined $fldSep or not defined $fldSepCtrl or
      66        
133             $fldSep ne $fldSepCtrl) {
134              
135 1         8 return undef;
136             }
137              
138             # Set field separator based on control segment
139 10         22 $self->{FIELD_SEPARATOR} = $fldSep;
140              
141             # Set other separators
142 10         14 $self->{COMPONENT_SEPARATOR} = $compSep;
143 10         15 $self->{SUBCOMPONENT_SEPARATOR} = $subCompSep;
144 10         18 $self->{ESCAPE_CHARACTER} = $esc;
145 10         16 $self->{REPETITION_SEPARATOR} = $repSep;
146              
147             # Do all segments
148             #
149 10         25 for (my $i = 0; $i < @segments; $i++) {
150              
151 19         195 my @fields = split('\\' . $self->{FIELD_SEPARATOR}, $segments[$i]);
152              
153 19         41 my $name = shift(@fields);
154              
155             # Now decompose fields if necessary, into refs to arrays
156             #
157 19         50 for (my $j = 0; $j < @fields; $j++) {
158              
159             # Skip control field
160 82 100 100     251 if ($i == 0 && $j == 0) {
161              
162 10         24 next;
163             }
164              
165             my @comps = split('\\' . $self->{COMPONENT_SEPARATOR},
166 72         427 $fields[$j]);
167              
168 72         174 for (my $k = 0; $k < @comps; $k++) {
169              
170             my @subComps = split('\\' .
171             $self->{SUBCOMPONENT_SEPARATOR},
172 50         274 $comps[$k]);
173              
174             # Make it a ref or just the value
175 50 100       109 if (@subComps <= 1) {
176 47         116 $comps[$k] = $subComps[0];
177             }
178             else {
179 3         10 $comps[$k] = \@subComps;
180             }
181              
182             }
183              
184 72 100       120 if (@comps <= 1) {
185 66         143 $fields[$j] = $comps[0];
186             }
187             else {
188 6         15 $fields[$j] = \@comps;
189             }
190             }
191              
192 19         24 my $seg;
193              
194             # untaint
195 19         34 my $segClass = "";
196              
197 19 100       66 if ($name =~ /^[A-Z][A-Z0-9]{2}$/) {
198 18         33 $segClass = "Net::HL7::Segments::$name";
199 18         45 $segClass =~ /^(.*)$/;
200 18         38 $segClass = $1;
201             }
202              
203             # Let's see whether it's a special segment
204             #
205 19 100 100     864 if ( $segClass && eval("require $segClass;") ) {
206 10         30 unshift(@fields, $self->{FIELD_SEPARATOR});
207 10         18 $seg = eval{ "$segClass"->new(\@fields); };
  10         44  
208             }
209             else {
210 9         43 $seg = new Net::HL7::Segment($name, \@fields);
211             }
212              
213 19 100       80 $seg || return undef;
214              
215 18         45 $self->addSegment($seg);
216             }
217             }
218              
219 21         88 return 1;
220             }
221              
222              
223             =pod
224              
225             =item B
226              
227             Add the segment. to the end of the message. The segment should be an
228             instance of L.
229              
230             =cut
231              
232             sub addSegment {
233              
234 42     42 1 70 my ($self, $segment) = @_;
235              
236 42 100       44 if (@{ $self->{SEGMENTS} } == 0) {
  42         97  
237 22         51 $self->_resetCtrl($segment);
238             }
239              
240 42         55 push( @{ $self->{SEGMENTS} }, $segment);
  42         116  
241             }
242              
243              
244             =pod
245              
246             =item B
247              
248             Insert the segment. The segment should be an instance of
249             L. If the index is not given,
250             nothing happens.
251              
252             =cut
253              
254             sub insertSegment {
255              
256 4     4 1 14 my ($self, $segment, $idx) = @_;
257              
258 4 50       10 (! defined $idx) && return;
259 4 100       5 ($idx > @{ $self->{SEGMENTS} }) && return;
  4         10  
260              
261 3 50       9 if ($idx == 0) {
    100          
262              
263 0         0 $self->_resetCtrl($segment);
264 0         0 unshift(@{ $self->{SEGMENTS} }, $segment);
  0         0  
265             }
266 3         6 elsif ($idx == @{ $self->{SEGMENTS} }) {
267              
268 1         2 push(@{ $self->{SEGMENTS} }, $segment);
  1         3  
269             }
270             else {
271 2         6 @{ $self->{SEGMENTS} } =
272 2         7 (@{ $self->{SEGMENTS} }[0..$idx-1],
273             $segment,
274 2         4 @{ $self->{SEGMENTS} }[$idx..@{ $self->{SEGMENTS} } -1]
  2         2  
  2         3  
275             );
276             }
277             }
278              
279              
280             =pod
281              
282             =item B
283              
284             Return the segment specified by $index. Segment count within the
285             message starts at 0.
286              
287             =cut
288              
289             sub getSegmentByIndex {
290              
291 150     150 1 595 my ($self, $index) = @_;
292              
293 150         275 return $self->{SEGMENTS}->[$index];
294             }
295              
296              
297             =pod
298              
299             =item B
300              
301             Return an array of all segments with the given name
302              
303             =cut
304              
305             sub getSegmentsByName {
306              
307 2     2 1 8 my ($self, $name) = @_;
308              
309 2         5 my @segments = ();
310              
311 2         3 foreach (@{ $self->{SEGMENTS} }) {
  2         5  
312 7 100       12 ($_->getName() eq $name) && push(@segments, $_);
313             }
314              
315 2         8 return @segments;
316             }
317              
318              
319             =pod
320              
321             =item B
322              
323             Remove the segment indexed by $index. If it doesn't exist, nothing
324             happens, if it does, all segments after this one will be moved one
325             index up.
326              
327             =cut
328              
329             sub removeSegmentByIndex {
330              
331 4     4 1 11 my ($self, $index) = @_;
332              
333 4 100       5 ($index < @{ $self->{SEGMENTS} }) && splice( @{ $self->{SEGMENTS} }, $index, 1);
  3         7  
  4         10  
334             }
335              
336              
337             =pod
338              
339             =item B
340              
341             Set the segment on index. If index is out of range, or not provided,
342             do nothing. Setting MSH on index 0 will revalidate field separator,
343             control characters and hl7 version, based on MSH(1), MSH(2) and
344             MSH(12).
345              
346             =cut
347              
348             sub setSegment {
349              
350 3     3 1 16 my ($self, $segment, $idx) = @_;
351              
352 3 100       10 (! defined $idx) && return;
353 2 50       3 ($idx > @{ $self->{SEGMENTS} }) && return;
  2         7  
354              
355 2 100 66     8 if ($segment->getName() eq "MSH" && $idx == 0) {
356              
357 1         9 $self->_resetCtrl($segment);
358             }
359              
360 2         4 @{ $self->{SEGMENTS} }[$idx] = $segment;
  2         5  
361             }
362              
363              
364             # After change of MSH, reset control fields
365             #
366             sub _resetCtrl {
367              
368 33     33   47 my ($self, $segment) = @_;
369              
370 33 50       75 if ($segment->getField(1)) {
371 33         67 $self->{FIELD_SEPARATOR} = $segment->getField(1);
372             }
373              
374 33 50       59 if ($segment->getField(2) =~ /(.)(.)(.)(.)/) {
375              
376 33         78 $self->{COMPONENT_SEPARATOR} = $1;
377 33         53 $self->{REPETITION_SEPARATOR} = $2;
378 33         52 $self->{ESCAPE_CHARACTER} = $3;
379 33         61 $self->{SUBCOMPONENT_SEPARATOR} = $4;
380             }
381              
382 33 100       64 if ($segment->getField(12)) {
383 17         32 $self->{HL7_VERSION} = $segment->getField(12);
384             }
385             }
386              
387              
388             =pod
389              
390             =item B
391              
392             Return an array containing all segments in the right order.
393              
394             =cut
395              
396             sub getSegments {
397              
398 0     0 1 0 my $self = shift;
399              
400 0         0 return @{ $self->{SEGMENTS} };
  0         0  
401             }
402              
403              
404             =pod
405              
406             =item B
407              
408             Return a string representation of this message. This can be used to
409             send the message over a socket to an HL7 server. To print to other
410             output, use the $pretty argument as some true value. This will not use
411             the default segment separator, but '\n' instead.
412              
413             =cut
414              
415             sub toString {
416              
417 10     10 1 40 my ($self, $pretty) = @_;
418 10         18 my $msg = "";
419              
420             # Make sure MSH(1) and MSH(2) are ok, even if someone has changed
421             # these values
422             #
423 10         18 my $msh = $self->{SEGMENTS}->[0];
424              
425 10         25 $self->_resetCtrl($msh);
426              
427 10         16 for (my $i = 0; $i < @{ $self->{SEGMENTS} }; $i++) {
  26         61  
428              
429 16         43 $msg .= $self->getSegmentAsString($i);
430              
431 16 100       36 $pretty ? ($msg .= "\n") : ($msg .= $self->{SEGMENT_SEPARATOR});
432             }
433              
434 10         90 return $msg;
435             }
436              
437              
438             =pod
439              
440             =item B
441              
442             Get the string representation of the segment, in the context of this
443             message. That means the string representation will use the message's
444             separators.
445              
446             =cut
447              
448             sub getSegmentAsString {
449              
450 20     20 1 30 my ($self, $index) = @_;
451              
452 20         38 my $seg = $self->getSegmentByIndex($index);
453              
454 20 50       48 $seg || return undef;
455              
456 20         65 my $segStr = $seg->getName() . $self->{FIELD_SEPARATOR};
457              
458 20 100       35 my $start = $seg->getName() eq "MSH" ? 2 : 1;
459              
460             {
461 7     7   46 no warnings;
  7         12  
  7         1194  
  20         27  
462              
463 20         43 foreach ($start..$seg->size()) {
464              
465 79         134 $segStr .= $self->getSegmentFieldAsString($index, $_);
466 79         119 $segStr .= $self->{FIELD_SEPARATOR};
467             }
468             }
469              
470 20         46 return $segStr;
471             }
472              
473              
474             =pod
475              
476             =item B
477              
478              
479             =cut
480              
481             sub getSegmentFieldAsString {
482              
483 81     81 1 125 my ($self, $segIndex, $fldIndex) = @_;
484              
485 81         111 my $seg = $self->getSegmentByIndex($segIndex);
486              
487 81 50       140 $seg || return undef;
488              
489 81         145 return $seg->getFieldAsString($fldIndex);
490             }
491              
492              
493             =pod
494              
495             =item B
496              
497             Remove the segment indexed by $name. If it doesn't exist, nothing
498             happens, if it does, all segments after this one will be moved one
499             index up.
500              
501             =back
502              
503             =cut
504              
505             sub removeSegmentByName {
506              
507 1     1 1 2 my ($self, $name) = @_;
508 1         2 my $i = 0;
509              
510 1         2 foreach (@{ $self->{SEGMENTS} }) {
  1         4  
511 2 100       6 if ($_->getName() eq $name) {
512 1         2 splice( @{ $self->{SEGMENTS} }, $i, 1);
  1         5  
513             }
514             else {
515 1         2 $i++;
516             }
517             }
518             }
519              
520              
521             1;
522              
523             =pod
524              
525             =head1 AUTHOR
526              
527             D.A.Dokter
528              
529             =head1 LICENSE
530              
531             Copyright (c) 2002 D.A.Dokter. All rights reserved. This program is
532             free software; you can redistribute it and/or modify it under the same
533             terms as Perl itself.
534              
535             =cut