File Coverage

blib/lib/Biblio/SICI/ItemSegment.pm
Criterion Covered Total %
statement 124 215 57.6
branch 41 136 30.1
condition 6 12 50.0
subroutine 14 18 77.7
pod 5 5 100.0
total 190 386 49.2


line stmt bran cond sub pod time code
1              
2             package Biblio::SICI::ItemSegment;
3             {
4             $Biblio::SICI::ItemSegment::VERSION = '0.04';
5             }
6              
7             # ABSTRACT: The item segment of a SICI
8              
9 3     3   16 use strict;
  3         6  
  3         104  
10 3     3   15 use warnings;
  3         7  
  3         242  
11 3     3   87 use 5.010001;
  3         11  
  3         116  
12              
13 3     3   132 use Moo;
  3         6  
  3         34  
14 3     3   1121 use Sub::Quote;
  3         6  
  3         173  
15              
16 3     3   3518 use Business::ISSN;
  3         7048  
  3         186  
17              
18 3     3   19 use Biblio::SICI;
  3         7  
  3         11719  
19             with 'Biblio::SICI::Role::ValidSegment', 'Biblio::SICI::Role::RecursiveLink';
20              
21              
22             has 'issn' => ( is => 'rw', trigger => 1, predicate => 1, clearer => 1, );
23              
24             sub _trigger_issn {
25 59     59   11627 my ( $self, $newVal ) = @_;
26 59         103 my @problems = ();
27              
28 59 50       275 if ( $newVal !~ m!\A[0-9X-]+\Z! ) {
29 0         0 push @problems, 'contains invalid characters';
30             }
31              
32 59 50       328 if ( $newVal !~ m!\A[0-9]{4}\-[0-9]{3}[0-9X]\Z! ) {
33 0         0 push @problems, 'structural error';
34             }
35              
36 59 50       143 unless (@problems) {
37 59 50       272 if ( my $is = Business::ISSN->new($newVal) ) {
38 59 50       4192 unless ( $is->is_valid ) {
39 0         0 push @problems, 'invalid issn';
40             }
41             }
42             }
43              
44 59 50       447 if (@problems) {
45 0         0 $self->log_problem_on( 'issn' => [@problems] );
46             }
47             else {
48 59         170 $self->clear_problem_on('issn');
49             }
50              
51 59         1162 return;
52             } ## end sub _trigger_issn
53              
54              
55             has 'chronology' => ( is => 'rw', trigger => 1, predicate => 1, clearer => 1, );
56              
57             sub _trigger_chronology {
58 58     58   2958 my ( $self, $newVal ) = @_;
59              
60             # TODO calendar schemes other than Gregorian may be used?!
61              
62 58         96 my @problems = ();
63              
64             # 1 YYYY
65             # 2 YYYY/YYYY with second YYYY > first YYYY
66             # 3 YYYYMM
67             # 4 YYYYMM/MM with second MM > first MM
68             # 5 YYYYMM/YYYYMM with second YYYY > first YYYY
69             # 6 YYYYMMDD
70             # 7 YYYYMMDD/DD with second DD > first DD
71             # 8 YYYYMMDD/MMDD with second MM > first MM
72             # 9 YYYYMMDD/YYYYMMDD with second YYYY > first YYYY
73              
74 58         84 my (%e) = ();
75              
76 58 100       498 if ( $newVal =~ /\A[0-9]{4}\Z/ ) {
    100          
    100          
    100          
    100          
    50          
    0          
    0          
    0          
77 11         29 $e{'1Y'} = $newVal;
78             }
79             elsif ( $newVal =~ /\A([0-9]{4})\/([0-9]{4})\Z/ ) {
80 1         4 $e{'1Y'} = $1;
81 1         3 $e{'2Y'} = $2;
82              
83 1 50       5 unless ( $e{'2Y'} > $e{'1Y'} ) {
84 0         0 push @problems, 'if specified, second year must be larger than first year';
85             }
86             }
87             elsif ( $newVal =~ /\A([0-9]{4})([0-9]{2})\Z/ ) {
88 24         88 $e{'1Y'} = $1;
89 24         69 $e{'1M'} = $2;
90             }
91             elsif ( $newVal =~ /\A([0-9]{4})([0-9]{2})\/([0-9]{2})\Z/ ) {
92 7         28 $e{'1Y'} = $1;
93 7         21 $e{'1M'} = $2;
94 7         23 $e{'2M'} = $3;
95              
96 7 50       28 unless ( $e{'2M'} > $e{'1M'} ) {
97 0         0 push @problems, 'if specified, second month must be larger than first month';
98             }
99             }
100             elsif ( $newVal =~ /\A([0-9]{4})([0-9]{2})\/([0-9]{4})([0-9]{2})\Z/ ) {
101 1         3 $e{'1Y'} = $1;
102 1         2 $e{'1M'} = $2;
103 1         4 $e{'2Y'} = $3;
104 1         5 $e{'2M'} = $4;
105              
106 1 50       6 unless ( $e{'2Y'} > $e{'1Y'} ) {
107 0         0 push @problems, 'if specified, second year must be larger than first year';
108             }
109             }
110             elsif ( $newVal =~ /\A([0-9]{4})([0-9]{2})([0-9]{2})\Z/ ) {
111 14         44 $e{'1Y'} = $1;
112 14         36 $e{'1M'} = $2;
113 14         33 $e{'1D'} = $3;
114             }
115             elsif ( $newVal =~ /\A([0-9]{4})([0-9]{2})([0-9]{2})\/([0-9]{2})\Z/ ) {
116 0         0 $e{'1Y'} = $1;
117 0         0 $e{'1M'} = $2;
118 0         0 $e{'1D'} = $3;
119 0         0 $e{'2D'} = $4;
120              
121 0 0       0 unless ( $e{'2D'} > $e{'1D'} ) {
122 0         0 push @problems, 'if specified, second day must be larger than first day';
123             }
124             }
125             elsif ( $newVal =~ /\A([0-9]{4})([0-9]{2})([0-9]{2})\/([0-9]{2})([0-9]{2})\Z/ ) {
126 0         0 $e{'1Y'} = $1;
127 0         0 $e{'1M'} = $2;
128 0         0 $e{'1D'} = $3;
129 0         0 $e{'2M'} = $4;
130 0         0 $e{'2D'} = $5;
131              
132 0 0       0 unless ( $e{'2M'} > $e{'1M'} ) {
133 0         0 push @problems, 'if specified, second month must be larger than first month';
134             }
135             }
136             elsif ( $newVal =~ /\A([0-9]{4})([0-9]{2})([0-9]{2})\/([0-9]{4})([0-9]{2})([0-9]{2})\Z/ ) {
137 0         0 $e{'1Y'} = $1;
138 0         0 $e{'1M'} = $2;
139 0         0 $e{'1D'} = $3;
140 0         0 $e{'2Y'} = $4;
141 0         0 $e{'2M'} = $5;
142 0         0 $e{'2D'} = $6;
143              
144 0 0       0 unless ( $e{'2Y'} > $e{'1Y'} ) {
145 0         0 push @problems, 'if specified, second year must be larger than first year';
146             }
147             }
148             else {
149 0         0 $self->log_problem_on( 'chronology', ['illegal chronology structure'] );
150 0         0 return;
151             }
152              
153             # lets accept pub dates up to one year in the future
154             # (relative to the date when this code is executed!)
155 58         2503 my (@time) = localtime(time);
156 58         160 my $yr = $time[5] + 1900;
157 58         237 my ( undef, undef, $decade, $year ) = split( '', $yr );
158 58 50       189 if ( $year == 9 ) {
159 0         0 $year = 0;
160 0         0 $decade += 1;
161             }
162             else {
163 58         78 $year += 1;
164             }
165 58         91 my $prevDecade = $decade - 1;
166              
167 58         126 for (qw( 2D 1D )) {
168 116 50 66     435 if ( exists $e{$_} and $e{$_} !~ /\A(?:[012][0-9]|3[01])\Z/ ) {
169 0 0       0 push @problems,
170             'illegal value for '
171             . ( $_ eq '2D' ? 'second' : 'first' )
172             . ' day: should be 01-31';
173             }
174             }
175 58         101 for (qw( 2M 1M )) {
176 116 50 66     614 if ( exists $e{$_} and $e{$_} !~ /\A(?:0[0-9]|1[012]|[23][1-4])\Z/ ) {
177 0 0       0 push @problems,
178             'illegal value for '
179             . ( $_ eq '2M' ? 'second' : 'first' )
180             . ' month: should be 00-12, or 21-24, or 31-34';
181             }
182             }
183 58         99 for (qw( 2Y 1Y )) {
184 116 50 66     683 if ( exists $e{$_}
185             and $e{$_} !~ /\A(?:1[0-9][0-9]{2}|20[0-$prevDecade][0-9]|20$decade[0-$year])\Z/o )
186             {
187 0 0       0 push @problems, 'illegal value for ' . ( $_ eq '2Y' ? 'second' : 'first' ) . ' year';
188             }
189             }
190              
191 58 50       186 if ( !@problems ) {
192 58         190 $self->clear_problem_on('chronology');
193             }
194             else {
195 0         0 $self->log_problem_on( 'chronology', \@problems );
196             }
197              
198 58         1230 return;
199             } ## end sub _trigger_chronology
200              
201              
202             has 'enumeration' => ( is => 'rw', predicate => 1, clearer => 1, trigger => 1, );
203              
204             sub _trigger_enumeration {
205 17     17   1654 my ( $self, $newVal ) = @_;
206              
207             # clear partial values
208 17         296 $self->clear_volume();
209 17         460 $self->clear_problem_on('volume');
210 17         293 $self->clear_issue();
211 17         438 $self->clear_problem_on('issue');
212 17         286 $self->clear_supplOrIdx();
213 17         443 $self->clear_problem_on('supplOrIdx');
214              
215 17 50       80 if ( $newVal !~ /\A[0-9A-Z:]*[+*]?\Z/ ) {
216 0         0 $self->log_problem_on( 'enumeration' => ['invalid characters used'] );
217             }
218 17         306 return;
219             }
220              
221              
222             has 'volume' => ( is => 'rw', predicate => 1, clearer => 1, trigger => 1, );
223              
224             sub _trigger_volume {
225 40     40   2558 my ( $self, $newVal ) = @_;
226              
227             # clear aggregate value
228 40         851 $self->clear_enumeration();
229 40         665 $self->clear_problem_on('enumeration');
230              
231 40         71 my @problems = ();
232              
233 40 50       190 if ( $newVal !~ m!\A[A-Z0-9/]+\Z! ) {
234 0         0 push @problems, 'contains invalid characters';
235             }
236              
237 40 50       176 if ( $newVal !~ m!\A[A-Z0-9]+(?:/[A-Z0-9]+)?\Z! ) {
238 0         0 push @problems, 'structural error';
239             }
240              
241 40 50       96 if (@problems) {
242 0         0 $self->log_problem_on( 'volume' => [@problems] );
243             }
244             else {
245 40         113 $self->clear_problem_on('volume');
246             }
247              
248 40         871 return;
249             } ## end sub _trigger_volume
250              
251              
252             has 'issue' => ( is => 'rw', predicate => 1, clearer => 1, trigger => 1, );
253              
254             sub _trigger_issue {
255 40     40   2435 my ( $self, $newVal ) = @_;
256              
257             # clear aggregate value
258 40         797 $self->clear_enumeration();
259 40         224 $self->clear_problem_on('enumeration');
260              
261 40         66 my @problems = ();
262              
263 40 50       168 if ( $newVal !~ m!\A[A-Z0-9/]+\Z! ) {
264 0         0 push @problems, 'contains invalid characters';
265             }
266              
267 40 50       160 if ( $newVal !~ m!\A[A-Z0-9]+(?:/[A-Z0-9]+)?\Z! ) {
268 0         0 push @problems, 'structural error';
269             }
270              
271 40 50       90 if (@problems) {
272 0         0 $self->log_problem_on( 'issue' => [@problems] );
273             }
274             else {
275 40         113 $self->clear_problem_on('issue');
276             }
277              
278 40         789 return;
279             } ## end sub _trigger_issue
280              
281              
282             has 'supplOrIdx' => ( is => 'rw', predicate => 1, clearer => 1, trigger => 1, );
283              
284             sub _trigger_supplOrIdx {
285 0     0   0 my ( $self, $newVal ) = @_;
286              
287             # clear aggregate value
288 0         0 $self->clear_enumeration();
289 0         0 $self->clear_problem_on('enumeration');
290              
291 0         0 my @problems = ();
292              
293 0 0       0 if ( length $newVal != 1 ) {
294 0         0 push @problems, 'too many characters (allowed: 1)';
295             }
296              
297 0 0 0     0 if ( $newVal ne '+' and $newVal ne '*' ) {
298 0         0 push @problems, 'contains invalid characters';
299             }
300              
301 0 0       0 if (@problems) {
302 0         0 $self->log_problem_on( 'supplOrIdx' => [@problems] );
303             }
304             else {
305 0         0 $self->clear_problem_on('supplOrIdx');
306             }
307              
308 0         0 return;
309             } ## end sub _trigger_supplOrIdx
310              
311              
312             sub year {
313 0     0 1 0 my $self = shift;
314              
315 0 0       0 return unless $self->has_chronology();
316              
317 0         0 my $c = $self->chronology;
318              
319             # 1 YYYY
320             # 2 YYYY/YYYY with second YYYY > first YYYY
321             # 3 YYYYMM
322             # 4 YYYYMM/MM with second MM > first MM
323             # 5 YYYYMM/YYYYMM with second YYYY > first YYYY
324             # 6 YYYYMMDD
325             # 7 YYYYMMDD/DD with second DD > first DD
326             # 8 YYYYMMDD/MMDD with second MM > first MM
327             # 9 YYYYMMDD/YYYYMMDD with second YYYY > first YYYY
328              
329 0 0       0 if ( $c =~ /\A[0-9]{4}\Z/ ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
330 0         0 return "$c";
331             }
332             elsif ( $c =~ /\A([0-9]{4})\/([0-9]{4})\Z/ ) {
333 0         0 return ( "$1", "$2" );
334             }
335             elsif ( $c =~ /\A([0-9]{4})(?:[0-9]{2})\Z/ ) {
336 0         0 return "$1";
337             }
338             elsif ( $c =~ /\A([0-9]{4})(?:[0-9]{2})\/(?:[0-9]{2})\Z/ ) {
339 0         0 return "$1";
340             }
341             elsif ( $c =~ /\A([0-9]{4})(?:[0-9]{2})\/([0-9]{4})(?:[0-9]{2})\Z/ ) {
342 0         0 return ( "$1", "$2" );
343             }
344             elsif ( $c =~ /\A([0-9]{4})(?:[0-9]{2})(?:[0-9]{2})\Z/ ) {
345 0         0 return "$1";
346             }
347             elsif ( $c =~ /\A([0-9]{4})(?:[0-9]{2})(?:[0-9]{2})\/(?:[0-9]{2})\Z/ ) {
348 0         0 return "$1";
349             }
350             elsif ( $c =~ /\A([0-9]{4})(?:[0-9]{2})(?:[0-9]{2})\/(?:[0-9]{2})(?:[0-9]{2})\Z/ ) {
351 0         0 return "$1";
352             }
353             elsif ( $c =~ /\A([0-9]{4})(?:[0-9]{2})(?:[0-9]{2})\/([0-9]{4})(?:[0-9]{2})(?:[0-9]{2})\Z/ ) {
354 0         0 return ( "$1", "$2" );
355             }
356              
357 0         0 return;
358             } ## end sub year
359              
360              
361             sub month {
362 0     0 1 0 my $self = shift;
363              
364 0 0       0 return unless $self->has_chronology();
365              
366 0         0 my $c = $self->chronology;
367              
368             # 3 YYYYMM
369             # 4 YYYYMM/MM with second MM > first MM
370             # 5 YYYYMM/YYYYMM with second YYYY > first YYYY
371             # 6 YYYYMMDD
372             # 7 YYYYMMDD/DD with second DD > first DD
373             # 8 YYYYMMDD/MMDD with second MM > first MM
374             # 9 YYYYMMDD/YYYYMMDD with second YYYY > first YYYY
375              
376 0 0       0 if ( $c =~ /\A(?:[0-9]{4})([0-9]{2})\Z/ ) {
    0          
    0          
    0          
    0          
    0          
    0          
377 0         0 return "$1";
378             }
379             elsif ( $c =~ /\A(?:[0-9]{4})([0-9]{2})\/([0-9]{2})\Z/ ) {
380 0         0 return ( "$1", "$2" );
381             }
382             elsif ( $c =~ /\A(?:[0-9]{4})([0-9]{2})\/(?:[0-9]{4})([0-9]{2})\Z/ ) {
383 0         0 return ( "$1", "$2" );
384             }
385             elsif ( $c =~ /\A(?:[0-9]{4})([0-9]{2})(?:[0-9]{2})\Z/ ) {
386 0         0 return "$1";
387             }
388             elsif ( $c =~ /\A(?:[0-9]{4})([0-9]{2})(?:[0-9]{2})\/(?:[0-9]{2})\Z/ ) {
389 0         0 return "$1";
390             }
391             elsif ( $c =~ /\A(?:[0-9]{4})([0-9]{2})(?:[0-9]{2})\/([0-9]{2})(?:[0-9]{2})\Z/ ) {
392 0         0 return ( "$1", "$2" );
393             }
394             elsif ( $c =~ /\A(?:[0-9]{4})([0-9]{2})(?:[0-9]{2})\/(?:[0-9]{4})([0-9]{2})(?:[0-9]{2})\Z/ ) {
395 0         0 return ( "$1", "$2" );
396             }
397              
398 0         0 return;
399             } ## end sub month
400              
401              
402             sub day {
403 0     0 1 0 my $self = shift;
404              
405 0 0       0 return unless $self->has_chronology();
406              
407 0         0 my $c = $self->chronology;
408              
409             # 6 YYYYMMDD
410             # 7 YYYYMMDD/DD with second DD > first DD
411             # 8 YYYYMMDD/MMDD with second MM > first MM
412             # 9 YYYYMMDD/YYYYMMDD with second YYYY > first YYYY
413              
414 0 0       0 if ( $c =~ /\A(?:[0-9]{4})(?:[0-9]{2})([0-9]{2})\Z/ ) {
    0          
    0          
    0          
415 0         0 return "$1";
416             }
417             elsif ( $c =~ /\A(?:[0-9]{4})(?:[0-9]{2})([0-9]{2})\/([0-9]{2})\Z/ ) {
418 0         0 return ( "$1", "$2" );
419             }
420             elsif ( $c =~ /\A(?:[0-9]{4})(?:[0-9]{2})([0-9]{2})\/(?:[0-9]{2})([0-9]{2})\Z/ ) {
421 0         0 return ( "$1", "$2" );
422             }
423             elsif ( $c =~ /\A(?:[0-9]{4})(?:[0-9]{2})([0-9]{2})\/(?:[0-9]{4})(?:[0-9]{2})([0-9]{2})\Z/ ) {
424 0         0 return ( "$1", "$2" );
425             }
426              
427 0         0 return;
428             } ## end sub day
429              
430              
431             sub to_string {
432 179     179 1 1256 my $self = shift;
433              
434 179         206 my $str = '';
435              
436 179 50       502 if ( $self->has_issn() ) {
437 179         3445 $str = $self->issn();
438             }
439              
440 179 100       1174 if ( $self->has_chronology() ) {
441 176         3337 $str .= '(' . $self->chronology() . ')';
442             }
443             else {
444 3         6 $str .= '()';
445             }
446              
447 179 100       1237 if ( $self->has_enumeration() ) {
448 52         940 $str .= $self->enumeration();
449             }
450             else {
451 127 100       322 if ( $self->has_volume() ) {
452 118         2300 $str .= $self->volume();
453 118 50       893 if ( $self->has_issue() ) {
454 118         2375 $str .= ':' . $self->issue();
455             }
456             }
457 127 50       967 if ( $self->has_supplOrIdx() ) {
458 0         0 $str .= $self->supplOrIdx();
459             }
460             }
461              
462 179         670 return $str;
463             } ## end sub to_string
464              
465              
466             sub reset {
467 58     58 1 448 my $self = shift;
468 58         1135 $self->clear_issn();
469 58         812 $self->clear_problem_on('issn');
470 58         2490 $self->clear_chronology();
471 58         655 $self->clear_problem_on('chronology');
472 58         1282 $self->clear_enumeration();
473 58         871 $self->clear_problem_on('enumeration');
474 58         1111 $self->clear_volume();
475 58         707 $self->clear_problem_on('volumne');
476 58         1089 $self->clear_issue();
477 58         736 $self->clear_problem_on('issue');
478 58         1125 $self->clear_supplOrIdx();
479 58         655 $self->clear_problem_on('supplOrIdx');
480 58         128 return;
481             }
482              
483              
484             1;
485              
486             __END__