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   4912 use strict;
  11         28  
  11         307  
4 11     11   69 use warnings;
  11         40  
  11         254  
5              
6 11     11   57 use Protocol::FIX;
  11         20  
  11         9098  
7              
8             our $VERSION = '0.08'; ## 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 20148 my ($class, $name, $type, $composites) = @_;
28              
29 2054 50       4867 die "composites array must be even"
30             if @$composites % 2;
31              
32 2054 50       3899 die "composites array must be non-empty"
33             unless @$composites;
34              
35 2054 50 33     10467 die "composite name must be defined"
36             if (!defined($name) || $name !~ /.+/);
37              
38 2054         6134 my @composites;
39             my @mandatory_composites;
40 2054         0 my %composite_by_name;
41 2054         0 my %component_for;
42              
43 2054         4601 for (my $idx = 0; $idx < @$composites; $idx += 2) {
44 39737         59458 my $c = $composites->[$idx];
45 39737         58407 my $required = $composites->[$idx + 1];
46 39737 50       73800 die "The object $idx must be a composite"
47             unless Protocol::FIX::is_composite($c);
48              
49 39737         64258 my $prerequisite_composite;
50 39737 100       76522 if ($c->{type} eq 'DATA') {
51             my $valid_definition = ($idx > 0)
52 2586   66     8450 && $composites->[$idx - 2]->{type} eq 'LENGTH';
53             die "The field type 'LENGTH' must appear before field " . $c->{name}
54 2586 100       4790 unless $valid_definition;
55 2585         3899 $prerequisite_composite = $composites->[$idx - 2];
56             }
57              
58 39736 100       79556 push @composites, $c, ($required ? 1 : 0);
59 39736 100       69535 push @mandatory_composites, $c->{name} if $required;
60 39736         114120 $composite_by_name{$c->{name}} = [$c, $prerequisite_composite];
61              
62 39736 100       109644 if (UNIVERSAL::isa($c, 'Protocol::FIX::Component')) {
63 4371         6972 my $sub_dependency = $c->{field_to_component};
64 4371         21194 for my $k (keys %$sub_dependency) {
65 33862 50       65769 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 33862         81373 $component_for{$k} = $sub_dependency->{$k};
73             }
74             }
75 39736 50       82527 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 39736         112595 $component_for{$c->{name}} = $name;
85              
86             }
87              
88 2053         9752 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         7787 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 7437 my ($self, $values) = @_;
111              
112 54 50 33     346 die "values must be non-empty even array"
      33        
113             if (ref($values) ne 'ARRAY') || !@$values || (@$values % 2);
114              
115 54         102 my @strings;
116              
117             my %used_composites;
118 54         147 for (my $idx = 0; $idx < @$values; $idx += 2) {
119 165         309 my $name = $values->[$idx];
120 165         262 my $value = $values->[$idx + 1];
121 165         422 my $c_info = $self->{composite_by_name}->{$name};
122 165 100       319 if (!$c_info) {
123 1         20 die "Composite '$name' is not available for " . $self->{type} . " '" . $self->{name} . "'";
124             }
125              
126 164         257 my $c = $c_info->[0];
127 164 100       435 if ($c->{type} eq 'DATA') {
128 5         9 my $c_length = $c_info->[1];
129             my $valid_sequence = ($idx > 0)
130 5   66     22 && $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         7 my $actual_length = length($value);
136 4 100       29 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         413 push @strings, $c->serialize($value);
141 158         529 $used_composites{$name} = 1;
142             }
143 47         87 for my $mandatory_name (@{$self->{mandatory_composites}}) {
  47         113  
144             die "'$mandatory_name' is mandatory for " . $self->{type} . " '" . $self->{name} . "'"
145 91 100       263 unless exists $used_composites{$mandatory_name};
146             }
147 43         234 return join $Protocol::FIX::SEPARATOR, @strings;
148             }
149              
150             1;