File Coverage

blib/lib/Google/ProtocolBuffers.pm
Criterion Covered Total %
statement 278 293 94.8
branch 66 94 70.2
condition 16 31 51.6
subroutine 43 43 100.0
pod 2 9 22.2
total 405 470 86.1


line stmt bran cond sub pod time code
1             package Google::ProtocolBuffers;
2            
3 13     13   428693 use 5.008008;
  13         54  
  13         533  
4 13     13   70 use warnings;
  13         26  
  13         439  
5 13     13   65 use strict;
  13         27  
  13         351  
6            
7 13     13   21171 use Google::ProtocolBuffers::Codec;
  13         44  
  13         2126  
8 13     13   97 use Google::ProtocolBuffers::Constants qw/:complex_types :labels/;
  13         23  
  13         2502  
9 13     13   15548 use Class::Accessor;
  13         49970  
  13         173  
10 13     13   471 use Math::BigInt;
  13         28  
  13         123  
11 13     13   4891 use Carp;
  13         25  
  13         763  
12 13     13   16355 use Data::Dumper;
  13         97382  
  13         5289  
13            
14             our $VERSION = "0.11";
15            
16             sub parsefile {
17 9     9 1 12616 my $self = shift;
18 9         27 my $proto_filename = shift;
19 9   50     46 my $opts = shift || {};
20            
21 9         73 return $self->_parse({file=>$proto_filename}, $opts);
22             }
23            
24             sub parse {
25 11     11 1 16536 my $self = shift;
26 11         31 my $proto_text = shift;
27 11   100     70 my $opts = shift || {};
28            
29 11         91 return $self->_parse({text=>$proto_text}, $opts);
30             }
31            
32             ## Positional access is slightly faster than named one.
33             ## Currently, it's in the same order as text in proto file
34             ## "optional" (LABEL) int32 (type) foo (name) = 1 (number) [default=...]
35             use constant {
36 13         22665 F_LABEL => 0,
37             F_TYPE => 1,
38             F_NAME => 2,
39             F_NUMBER => 3,
40             F_DEFAULT => 4,
41 13     13   157 };
  13         31  
42            
43             sub _parse {
44 20     20   51 my $self = shift;
45 20         40 my $source = shift;
46 20         41 my $opts = shift;
47            
48 20         17855 require 'Google/ProtocolBuffers/Compiler.pm';
49 20         244 my $types = Google::ProtocolBuffers::Compiler->parse($source, $opts);
50            
51             ##
52             ## 1. Create enums - they will be used as default values for fields
53             ##
54 20         173397 my @created_classes;
55 20         313 while (my ($type_name, $desc) = each %$types) {
56 340 100       1468 next unless $desc->{kind} eq 'enum';
57 48         265 my $class_name = $self->_get_class_name_for($type_name, $opts);
58 48         617 $self->create_enum($class_name, $desc->{fields});
59 48         243 push @created_classes, $class_name;
60             }
61            
62             ##
63             ## 2. Create groups and messages,
64             ## Fill default values of fields and convert their
65             ## types (my_package.message_a) into Perl classes names (MyPackage::MessageA)
66             ##
67 20         129 while (my ($type_name, $desc) = each %$types) {
68 340         650 my $kind = $desc->{kind};
69 340         435 my @fields;
70            
71 340 100       1185 if ($kind eq 'enum') {
    100          
    50          
72 48         201 next;
73             } elsif ($kind eq 'group') {
74 57         97 push @fields, @{$desc->{fields}};
  57         145  
75             } elsif ($kind eq 'message') {
76 235         330 push @fields, @{$desc->{fields}};
  235         884  
77            
78             ##
79             ## Get names for extensions fields.
80             ## Original (full quilified) name is like 'package.MessageA.field'.
81             ## If 'simple_extensions' is true, it will be cut to the last element: 'field'.
82             ## Otherwise, it will be enclosed in brackets and all part common to message type
83             ## will be removed, e.g. for message 'package.MessageB' it will be '[MessageA.field]'
84             ## If message is 'other_package.MessageB', it will be '[package.MessageA.field]'
85             ##
86 235         340 foreach my $e (@{$desc->{extensions}}) {
  235         644  
87 658         991 my $field_name = $e->[F_NAME];
88 658         634 my $new_name;
89 658 100       1429 if ($opts->{simple_extensions}) {
90 6 100       66 $new_name = ($field_name =~ /\.(\w+)$/) ? $1 : $field_name;
91             } else {
92             ## remove common identifiers from start of f.q.i.
93 652         5822 my @type_idents = split qr/\./, $type_name;
94 652         3265 my @field_idents = split qr/\./, $field_name;
95 652   33     2982 while (@type_idents && @field_idents) {
96 1308 100       2838 last if $type_idents[0] ne $field_idents[0];
97 656         767 shift @type_idents;
98 656         5473 shift @field_idents;
99             }
100 652 50       1173 die "Can't create name for extension field '$field_name' in '$type_name'"
101             unless @field_idents;
102 652         1780 $new_name = '[' . join('.', @field_idents) . ']';
103             }
104 658         984 $e->[F_NAME] = $new_name;
105 658         1197 push @fields, $e;
106             }
107             } else {
108 0         0 die;
109             }
110            
111             ##
112             ## Replace proto type names by Perl classes names
113             ##
114 292         2858 foreach my $f (@fields) {
115 2078         3093 my $type = $f->[F_TYPE];
116 2078 100       8524 if ($type !~ /^\d+$/) {
117             ## not a primitive type
118 461         1126 $f->[F_TYPE] = $self->_get_class_name_for($type, $opts);
119             }
120             }
121            
122             ##
123             ## Default values: replace references to enum idents by their values
124             ##
125 292         512 foreach my $f (@fields) {
126 2078         2365 my $default_value = $f->[F_DEFAULT];
127 2078 100 100     6499 if ($default_value && ref $default_value) {
    100          
128             ## this default value is a literal
129 371 50       816 die "Unknown default value " . Data::Dumper::Dumper($default_value)
130             unless ref($default_value) eq 'HASH';
131 371         1108 $f->[F_DEFAULT] = $default_value->{value};
132             } elsif ($default_value) {
133             ## this default is an enum value
134 55         314 my ($enum_name, $enum_field_name) = ($default_value =~ /(.*)\.(\w+)$/);
135 55         242 my $class_name = $self->_get_class_name_for($enum_name, $opts);
136 13     13   104 no strict 'refs';
  13         35  
  13         951  
137 55         89 $f->[F_DEFAULT] = &{"${class_name}::$enum_field_name"};
  55         304  
138 13     13   85 use strict;
  13         34  
  13         20540  
139             }
140             }
141            
142             ##
143             ## Create Perl classes
144             ##
145 292         3982 my $class_name = $self->_get_class_name_for($type_name, $opts);
146 292 100       787 if ($kind eq 'message') {
    50          
147 235         745 $self->create_message($class_name, \@fields, $opts);
148             } elsif ($kind eq 'group') {
149 57         208 $self->create_group($class_name, \@fields, $opts);
150             }
151 291         34583 push @created_classes, $class_name;
152             }
153            
154             ## Generate Perl code of created classes
155 19 100       94 if ($opts->{generate_code}) {
156 3         270797 require 'Google/ProtocolBuffers/CodeGen.pm';
157 3         10 my $fh;
158 3 50       20 if (!ref($opts->{generate_code})) {
159 3 50       586 open($fh, ">$opts->{generate_code}")
160             or die "Can't write to '$opts->{generate_code}': $!";
161             } else {
162 0         0 $fh = $opts->{generate_code};
163             }
164            
165 3 100       23 my $package_str = ($opts->{'package_name'}) ?
166             "package $opts->{'package_name'};" : "";
167            
168 3 100       18 my $source_str = ($source->{'file'}) ?
169             "$source->{'file'}" : "inline text";
170            
171 3         1209 print $fh <<"HEADER";
172             # Generated by the protocol buffer compiler (protoc-perl) DO NOT EDIT!
173             # source: $source_str
174            
175             $package_str
176            
177             use strict;
178             use warnings;
179            
180             use Google::ProtocolBuffers;
181             {
182             HEADER
183 3         13 foreach my $class_name (@created_classes) {
184 39         519 print $fh $class_name->getPerlCode($opts);
185             }
186 3         400 print $fh "}\n1;\n";
187             }
188 19         1691 return @created_classes;
189             }
190            
191             # Google::ProtocolBuffers->create_message(
192             # 'AccountRecord',
193             # [
194             # ## required string name = 1;
195             # [LABEL_REQUIRED, TYPE_STRING, 'name', 1 ],
196             # [LABEL_OPTIONAL, TYPE_INT32, 'id', 2 ],
197             # ],
198             # );
199             sub create_message {
200 263     263 0 18643 my $self = shift;
201 263         343 my $class_name = shift;
202 263         1445 my $fields = shift;
203 263         322 my $opts = shift;
204            
205 263         1216 return $self->_create_message_or_group(
206             $class_name, $fields, $opts,
207             'Google::ProtocolBuffers::Message'
208             );
209             }
210            
211             sub create_group {
212 63     63 0 602 my $self = shift;
213 63         90 my $class_name = shift;
214 63         91 my $fields = shift;
215 63         85 my $opts = shift;
216            
217 63         175 return $self->_create_message_or_group(
218             $class_name, $fields, $opts,
219             'Google::ProtocolBuffers::Group'
220             );
221             }
222            
223             sub _create_message_or_group {
224 326     326   482 my $self = shift;
225 326         460 my $class_name = shift;
226 326         373 my $fields = shift;
227 326         358 my $opts = shift;
228 326         419 my $base_class = shift;
229            
230             ##
231             ## Sanity checks
232             ## 1. Class name must be a valid Perl class name
233             ## (should we check that this class doesn't exist yet?)
234             ##
235 326 50       1726 die "Invalid class name: '$class_name'"
236             unless $class_name =~ /^[a-z_]\w*(?:::[a-z_]\w*)*$/i;
237            
238             ##
239             ##
240 326         402 my (%field_names, %field_numbers);
241 326         561 foreach my $f (@$fields) {
242 2304         4569 my ($label, $type_name, $name, $field_number, $default_value) = @$f;
243 2304 50       7087 die Dumper $f unless $name;
244            
245             ##
246             ## field names must be valid identifiers and be unique
247             ##
248 2304 50 33     12902 die "Invalid field name: '$name'"
249             unless $name && $name =~ /^\[?[a-z_][\w\.]*\]?$/i;
250 2304 100       7159 if ($field_names{$name}++) {
251 1         317 die "Field '$name' is defined more than once";
252             }
253            
254             ##
255             ## field number must be positive and unique
256             ##
257 2303 50       4404 die "Invalid field number: $field_number" unless $field_number>0;
258 2303 50       5998 if ($field_numbers{$field_number}++) {
259 0         0 die "Field number $field_number is used more than once";
260             }
261            
262             ## type is either a number (for primitive types)
263             ## or a class name. Can't check that complex $type
264             ## is valid, because it may not exist yet.
265 2303 50       4063 die "Field '$name' doesn't has a type" unless $type_name;
266 2303 100       6601 if ($type_name =~/^\d+$/) {
267             ## ok, this is an ID of primitive type
268             } else {
269 511 50       2223 die "Type '$type_name' is not valid Perl class name"
270             unless $type_name =~ /^[a-z_]\w*(?:::[a-z_]\w*)*$/i;
271             }
272            
273 2303 50 100     9908 die "Unknown label value: $label"
      66        
274             unless $label==LABEL_OPTIONAL || $label==LABEL_REQUIRED || $label==LABEL_REPEATED;
275             }
276            
277            
278             ## Make a copy of values and sort them so that field_numbers increase,
279             ## this is a requirement of protocol
280             ## Postitional addressation of field parts is sucks, TODO: replace by hash
281 325         1507 my @field_list = sort { $a->[F_NUMBER] <=> $b->[F_NUMBER] } map { [@$_] } @$fields;
  2277         3257  
  2302         6276  
282 325         632 my %fields_by_field_name = map { $_->[F_NAME] => $_ } @field_list;
  2302         5014  
283 325         715 my %fields_by_field_number = map { $_->[F_NUMBER] => $_ } @field_list;
  2302         4698  
284            
285 13     13   93 no strict 'refs';
  13         30  
  13         1833  
286 325         704 @{"${class_name}::ISA"} = $base_class;
  325         7967  
287 325     252   1619 *{"${class_name}::_pb_fields_list"} = sub { \@field_list };
  325         2807  
  252         764  
288 325     50   1406 *{"${class_name}::_pb_fields_by_name"} = sub { \%fields_by_field_name };
  325         1569  
  50         144  
289 325     282   1497 *{"${class_name}::_pb_fields_by_number"} = sub { \%fields_by_field_number };
  325         1782  
  282         749  
290 13     13   727 use strict;
  13         29  
  13         530  
291            
292 325 100       1590 if ($opts->{create_accessors}) {
293 13     13   63 no strict 'refs';
  13         22  
  13         986  
294 163         193 push @{"${class_name}::ISA"}, 'Class::Accessor';
  163         2730  
295 163         587 *{"${class_name}::get"} = \&Google::ProtocolBuffers::get;
  163         2187  
296 163         278 *{"${class_name}::set"} = \&Google::ProtocolBuffers::set;
  163         818  
297 13     13   86 use strict;
  13         25  
  13         5214  
298            
299 163 50       496 if ($opts->{follow_best_practice}) {
300 0         0 $class_name->follow_best_practice;
301             }
302 163         316 my @accessors = grep { /^[a-z_]\w*$/i } map { $_->[2] } @$fields;
  1120         5098  
  1120         1990  
303 163         1918 $class_name->mk_accessors(@accessors);
304             }
305             }
306            
307             sub create_enum {
308 53     53 0 8833 my $self = shift;
309 53         75 my $class_name = shift;
310 53         80 my $fields = shift;
311 53         72 my $options = shift;
312            
313             ##
314             ## Sanity checks
315             ## 1. Class name must be a valid Perl class name
316             ## (should we check that this class doesn't exist yet?)
317             ## 2. Field names must be valid identifiers and be unique
318             ##
319 53 50       395 die "Invalid class name: '$class_name'"
320             unless $class_name =~ /^[a-z_]\w*(?:::[a-z_]\w*)*$/i;
321 53         78 my %names;
322 53         123 foreach my $f (@$fields) {
323 219         425 my ($name, $value) = @$f;
324 219 50 33     1271 die "Invalid field name: '$name'"
325             unless $name && $name =~ /^[a-z_]\w*$/i;
326 219 50       833 if ($names{$name}++) {
327 0         0 die "Field '$name' is defined more than once";
328             }
329             }
330            
331             ## base class and constants export
332 13     13   99 no strict 'refs';
  13         43  
  13         1012  
333 53         99 @{"${class_name}::ISA"} = "Google::ProtocolBuffers::Enum";
  53         1522  
334 53         130 %{"${class_name}::EXPORT_TAGS"} = ('constants'=>[]);
  53         509  
335 13     13   107 use strict;
  13         24  
  13         613  
336            
337             ## create the constants
338 53         116 foreach my $f (@$fields) {
339 219         348 my ($name, $value) = @$f;
340 13     13   59 no strict 'refs';
  13         21  
  13         1150  
341 219     112   869 *{"${class_name}::$name"} = sub { $value };
  219         3279  
  112         5206  
342 219         268 push @{ ${"${class_name}::EXPORT_TAGS"}{'constants'} }, $name;
  219         229  
  219         822  
343 219         270 push @{"${class_name}::EXPORT_OK"}, $name;
  219         809  
344 13     13   60 use strict;
  13         36  
  13         675  
345             }
346            
347             ## create a copy of fields for introspection/code generation
348 53         122 my @fields = map { [@$_] } @$fields;
  219         519  
349 13     13   58 no strict 'refs';
  13         19  
  13         10673  
350 53     5   179 *{"${class_name}::_pb_fields_list"} = sub { \@fields };
  53         376  
  5         16  
351            
352             }
353            
354             ##
355             ## Accessors
356             ##
357             sub getExtension {
358 14     14 0 551 my $self = shift;
359 14 50       39 my $data = (ref $self) ? $self : shift();
360 14         22 my $extension_name = shift;
361            
362 14         34 $extension_name =~ s/::/./g;
363 14         30 my $key = "[$extension_name]";
364            
365 14         38 my $field = $self->_pb_fields_by_name->{$key};
366 14 100       35 if ($field) {
367 13 100       91 return (exists $data->{$key}) ? $data->{$key} : $field->[F_DEFAULT];
368             } else {
369 1   33     5 my $class_name = ref $self || $self;
370 1         139 die "There is no extension '$extension_name' in '$class_name'";
371             }
372             }
373            
374             sub setExtension {
375 5     5 0 10 my $self = shift;
376 5 50       16 my $data = (ref $self) ? $self : shift();
377 5         9 my $extension_name = shift;
378 5         8 my $value = shift;
379            
380 5         10 $extension_name =~ s/::/./g;
381 5         13 my $key = "[$extension_name]";
382            
383 5 100       14 if ($self->_pb_fields_by_name->{$key}) {
384 4         14 $data->{$key} = $value;
385             } else {
386 1   33     6 my $class_name = ref $self || $self;
387 1         378 die "There is no extension '$extension_name' in '$class_name'";
388             }
389             }
390            
391             ##
392             ## This is for Class::Accessor read-accessors, will be
393             ## copied to classes from Message/Group.
394             ## If no value is set, the default one will be returned.
395             ##
396             sub get {
397 54     54 0 10553 my $self = shift;
398            
399 54 50       159 if (@_==1) {
    0          
400             ## checking that $self->{$_[0]} exists is not enough,
401             ## since undef value may be set via Class::Accessor's new, e.g:
402             ## my $data = My::Message->new({ name => undef })
403 54 100       300 return $self->{$_[0]} if defined $self->{$_[0]};
404 31         72 my $field = $self->_pb_fields_by_name->{$_[0]};
405 31         185 return $field->[F_DEFAULT];
406             } elsif (@_>1) {
407 0         0 my @rv;
408             my $fields;
409 0         0 foreach my $key (@_) {
410 0 0       0 if (defined $self->{$key}) {
411 0         0 push @rv, $self->{$key};
412             } else {
413 0   0     0 $fields ||= $self->_pb_fields_by_name;
414 0         0 push @rv, $fields->{$key}->[F_DEFAULT];
415             }
416             }
417 0         0 return @rv;
418             } else {
419 0         0 Carp::confess("Wrong number of arguments received.");
420             }
421             }
422            
423             sub set {
424 15     15 0 172 my $self = shift;
425 15         23 my $key = shift;
426            
427 15 50       50 if (@_==1) {
    0          
428 15 100       34 if (defined $_[0]) {
429 14         73 $self->{$key} = $_[0];
430             } else {
431 1         4 delete $self->{$key};
432             }
433             } elsif (@_>1) {
434 0         0 $self->{$key} = [@_];
435             } else {
436 0         0 Carp::confess("Wrong number of arguments received.");
437             }
438             }
439            
440             sub _get_class_name_for{
441 856     856   1261 my $self = shift;
442 856         1024 my $type_name = shift;
443 856         942 my $opts = shift;
444            
445 856 100       1717 if ($opts->{no_camel_case}) {
446 1         2 my $class_name = $type_name;
447 1         8 $class_name =~ s/\./::/g;
448 1         4 return $class_name;
449             } else {
450 855         5404 my @idents = split qr/\./, $type_name;
451 855         2379 foreach (@idents) {
452 1940         4837 s/_(.)/uc($1)/ge;
  982         2805  
453 1940         4673 $_ = "\u$_";
454             }
455 855         3984 return join("::", @idents);
456             }
457             }
458            
459             package Google::ProtocolBuffers::Message;
460 13     13   102 no warnings 'once';
  13         23  
  13         2963  
461             ## public
462             *encode = \&Google::ProtocolBuffers::Codec::encode;
463             *decode = \&Google::ProtocolBuffers::Codec::decode;
464             *setExtension = \&Google::ProtocolBuffers::setExtension;
465             *getExtension = \&Google::ProtocolBuffers::getExtension;
466             *getPerlCode = \&Google::ProtocolBuffers::CodeGen::generate_code_of_message_or_group;
467             ## internal
468             ## _pb_complex_type_kind can be removed and $class->isa('Google::ProtocolBuffers::Message')
469             ## can be used instead, but current implementation is faster
470 315     315   1013 sub _pb_complex_type_kind { Google::ProtocolBuffers::Constants::MESSAGE() }
471             # _pb_fields_list ## These 3 methods are created in
472             # _pb_fields_by_name ## namespace of derived class
473             # _pb_fields_by_number
474            
475             package Google::ProtocolBuffers::Group;
476             *setExtension = \&Google::ProtocolBuffers::setExtension;
477             *getExtension = \&Google::ProtocolBuffers::getExtension;
478             *getPerlCode = \&Google::ProtocolBuffers::CodeGen::generate_code_of_message_or_group;
479 40     40   143 sub _pb_complex_type_kind { Google::ProtocolBuffers::Constants::GROUP() }
480             #_pb_fields_list
481             #_pb_fields_by_name
482             #_pb_fields_by_number
483            
484             package Google::ProtocolBuffers::Enum;
485 13     13   78 use base 'Exporter';
  13         20  
  13         2117  
486             *getPerlCode = \&Google::ProtocolBuffers::CodeGen::generate_code_of_enum;
487 26     26   62 sub _pb_complex_type_kind { Google::ProtocolBuffers::Constants::ENUM() }
488             #_pb_fields_list
489            
490             1;
491            
492             __END__