File Coverage

blib/lib/Protocol/FIX/BaseComposite.pm
Criterion Covered Total %
statement 59 61 96.7
branch 27 34 79.4
condition 7 15 46.6
subroutine 5 5 100.0
pod 2 2 100.0
total 100 117 85.4


line stmt bran cond sub pod time code
1             package Protocol::FIX::BaseComposite;
2              
3 11     11   4694 use strict;
  11         29  
  11         303  
4 11     11   58 use warnings;
  11         36  
  11         262  
5              
6 11     11   55 use Protocol::FIX;
  11         21  
  11         9065  
7              
8             our $VERSION = '0.07'; ## VERSION
9              
10             =head1 NAME
11              
12             Protocol::FIX::BaseComposite - base class for Component, Group and Message
13              
14             =cut
15              
16             =head1 METHODS (for protocol developers)
17              
18             =head3 new
19              
20             new($class, $name, $type, $composites)
21              
22             Creates new BaseComposite (performed by Protocol, when it parses XML definition)
23              
24             =cut
25              
26             sub new {
27 2054     2054 1 20901 my ($class, $name, $type, $composites) = @_;
28              
29 2054 50       5414 die "composites array must be even"
30             if @$composites % 2;
31              
32 2054 50       3880 die "composites array must be non-empty"
33             unless @$composites;
34              
35 2054 50 33     10931 die "composite name must be defined"
36             if (!defined($name) || $name !~ /.+/);
37              
38 2054         6105 my @composites;
39             my @mandatory_composites;
40 2054         0 my %composite_by_name;
41 2054         0 my %component_for;
42              
43 2054         4606 for (my $idx = 0; $idx < @$composites; $idx += 2) {
44 39730         60190 my $c = $composites->[$idx];
45 39730         58115 my $required = $composites->[$idx + 1];
46 39730 50       74409 die "The object $idx must be a composite"
47             unless Protocol::FIX::is_composite($c);
48              
49 39730         65478 my $prerequisite_composite;
50 39730 100       77076 if ($c->{type} eq 'DATA') {
51             my $valid_definition = ($idx > 0)
52 2586   66     9661 && $composites->[$idx - 2]->{type} eq 'LENGTH';
53             die "The field type 'LENGTH' must appear before field " . $c->{name}
54 2586 100       4901 unless $valid_definition;
55 2585         4312 $prerequisite_composite = $composites->[$idx - 2];
56             }
57              
58 39729 100       78655 push @composites, $c, ($required ? 1 : 0);
59 39729 100       68765 push @mandatory_composites, $c->{name} if $required;
60 39729         112470 $composite_by_name{$c->{name}} = [$c, $prerequisite_composite];
61              
62 39729 100       110483 if (UNIVERSAL::isa($c, 'Protocol::FIX::Component')) {
63 4364         6793 my $sub_dependency = $c->{field_to_component};
64 4364         20922 for my $k (keys %$sub_dependency) {
65 33547 50       65497 if (exists $component_for{$k}) {
66             die( "Ambiguity when constructing component '$name': '$k' already points to '"
67             . $component_for{$k}
68             . "', trying to add another pointer to '"
69 0         0 . $sub_dependency->{$k}
70             . "'");
71             }
72 33547         79605 $component_for{$k} = $sub_dependency->{$k};
73             }
74             }
75 39729 50       83889 if (exists $component_for{$c->{name}}) {
76             die( "Ambiguity when constructing component '$name': '"
77             . $c->{name}
78             . "' already points to '"
79             . $component_for{$c->{name}}
80 0         0 . "', trying to add another pointer to '"
81             . $name
82             . "'");
83             }
84 39729         112535 $component_for{$c->{name}} = $name;
85              
86             }
87              
88 2053         13496 my $obj = {
89             name => $name,
90             type => $type,
91             composites => \@composites,
92             composite_by_name => \%composite_by_name,
93             mandatory_composites => \@mandatory_composites,
94             field_to_component => \%component_for,
95             };
96              
97 2053         7956 return bless $obj, $class;
98             }
99              
100             =head3 serialize
101              
102             serialize($self, $values)
103              
104             Serializes array of C<$values>. Not for end-user usage. Please, refer
105             L
106              
107             =cut
108              
109             sub serialize {
110 54     54 1 6179 my ($self, $values) = @_;
111              
112 54 50 33     386 die "values must be non-empty even array"
      33        
113             if (ref($values) ne 'ARRAY') || !@$values || (@$values % 2);
114              
115 54         99 my @strings;
116              
117             my %used_composites;
118 54         167 for (my $idx = 0; $idx < @$values; $idx += 2) {
119 165         309 my $name = $values->[$idx];
120 165         290 my $value = $values->[$idx + 1];
121 165         339 my $c_info = $self->{composite_by_name}->{$name};
122 165 100       340 if (!$c_info) {
123 1         15 die "Composite '$name' is not available for " . $self->{type} . " '" . $self->{name} . "'";
124             }
125              
126 164         268 my $c = $c_info->[0];
127 164 100       424 if ($c->{type} eq 'DATA') {
128 5         10 my $c_length = $c_info->[1];
129             my $valid_sequence = ($idx > 0)
130 5   66     23 && $self->{composite_by_name}->{$values->[$idx - 2]}->[0] == $c_length;
131              
132 5 100       22 die "The field '" . $c_length->{name} . "' must precede '" . $c->{name} . "'"
133             unless $valid_sequence;
134              
135 4         9 my $actual_length = length($value);
136 4 100       22 die "The length field '" . $c_length->{name} . "' ($actual_length) isn't equal previously declared (" . $values->[$idx - 1] . ")"
137             unless $values->[$idx - 1] == $actual_length;
138             }
139              
140 162         437 push @strings, $c->serialize($value);
141 158         557 $used_composites{$name} = 1;
142             }
143 47         79 for my $mandatory_name (@{$self->{mandatory_composites}}) {
  47         107  
144             die "'$mandatory_name' is mandatory for " . $self->{type} . " '" . $self->{name} . "'"
145 91 100       297 unless exists $used_composites{$mandatory_name};
146             }
147 43         214 return join $Protocol::FIX::SEPARATOR, @strings;
148             }
149              
150             1;