line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright 1998-2019, Paul Johnson (paul@pjcj.net) |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# This software is free. It is licensed under the same terms as Perl itself. |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# The latest version of this software should be available from my homepage: |
6
|
|
|
|
|
|
|
# http://www.pjcj.net |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# documentation at __END__ |
9
|
|
|
|
|
|
|
|
10
|
11
|
|
|
11
|
|
58
|
use strict; |
|
11
|
|
|
|
|
18
|
|
|
11
|
|
|
|
|
418
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
require 5.005; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
package Gedcom::Record; |
15
|
|
|
|
|
|
|
|
16
|
11
|
|
|
11
|
|
48
|
use vars qw($VERSION @ISA $AUTOLOAD); |
|
11
|
|
|
|
|
12
|
|
|
11
|
|
|
|
|
658
|
|
17
|
|
|
|
|
|
|
$VERSION = "1.21"; |
18
|
|
|
|
|
|
|
@ISA = qw( Gedcom::Item ); |
19
|
|
|
|
|
|
|
|
20
|
11
|
|
|
11
|
|
58
|
use Carp; |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
592
|
|
21
|
11
|
|
|
11
|
|
666
|
BEGIN { eval "use Date::Manip" } # We'll use this if it is available |
|
11
|
|
|
11
|
|
4113
|
|
|
11
|
|
|
|
|
1312154
|
|
|
11
|
|
|
|
|
1458
|
|
22
|
|
|
|
|
|
|
|
23
|
11
|
|
|
11
|
|
92
|
use Gedcom::Item 1.21; |
|
11
|
|
|
|
|
179
|
|
|
11
|
|
|
|
|
265
|
|
24
|
11
|
|
|
11
|
|
6479
|
use Gedcom::Comparison 1.21; |
|
11
|
|
|
|
|
133
|
|
|
11
|
|
|
|
|
362
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
BEGIN |
27
|
|
|
|
|
|
|
{ |
28
|
11
|
|
|
11
|
|
4835
|
use subs keys %Gedcom::Funcs; |
|
11
|
|
|
|
|
222
|
|
|
11
|
|
|
|
|
894
|
|
29
|
11
|
|
|
11
|
|
20978
|
*tag_record = \&Gedcom::Item::get_item; |
30
|
11
|
|
|
|
|
27
|
*delete_record = \&Gedcom::Item::delete_item; |
31
|
11
|
|
|
|
|
1174
|
*get_record = \&record; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
0
|
|
|
sub DESTROY {} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub AUTOLOAD { |
37
|
26
|
|
|
26
|
|
254
|
my ($self) = @_; # don't change @_ because of the goto |
38
|
26
|
|
|
|
|
49
|
my $func = $AUTOLOAD; |
39
|
|
|
|
|
|
|
# print "autoloading $func\n"; |
40
|
26
|
|
|
|
|
153
|
$func =~ s/^.*:://; |
41
|
26
|
50
|
|
|
|
128
|
carp "Undefined subroutine $func called" unless $Gedcom::Funcs{lc $func}; |
42
|
11
|
|
|
11
|
|
71
|
no strict "refs"; |
|
11
|
|
|
|
|
26
|
|
|
11
|
|
|
|
|
35533
|
|
43
|
|
|
|
|
|
|
*$func = sub { |
44
|
3113
|
|
|
3113
|
|
41373
|
my $self = shift; |
45
|
3113
|
|
|
|
|
3639
|
my ($count) = @_; |
46
|
3113
|
|
|
|
|
2999
|
my $v; |
47
|
|
|
|
|
|
|
# print "[[ $func ]]\n"; |
48
|
3113
|
100
|
|
|
|
3751
|
if (wantarray) { |
49
|
|
|
|
|
|
|
return map { |
50
|
3106
|
|
|
|
|
5722
|
$_ && |
51
|
3091
|
100
|
66
|
|
|
4390
|
do { $v = $_->full_value; defined $v && length $v ? $v : $_ } |
|
3091
|
50
|
|
|
|
7295
|
|
|
3091
|
|
|
|
|
14748
|
|
52
|
|
|
|
|
|
|
} $self->record([$func, $count]); |
53
|
|
|
|
|
|
|
} else { |
54
|
7
|
|
|
|
|
41
|
my $r = $self->record([$func, $count]); |
55
|
|
|
|
|
|
|
return $r && |
56
|
7
|
|
33
|
|
|
36
|
do { $v = $r->full_value; defined $v && length $v ? $v : $r } |
57
|
|
|
|
|
|
|
} |
58
|
26
|
|
|
|
|
390
|
}; |
59
|
26
|
|
|
|
|
117
|
goto &$func |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub record { |
63
|
3219
|
|
|
3219
|
1
|
4629
|
my $self = shift; |
64
|
3219
|
|
|
|
|
3687
|
my @records = ($self); |
65
|
3219
|
100
|
|
|
|
3878
|
for my $func (map { ref() ? $_ : split } @_) { |
|
3314
|
|
|
|
|
6414
|
|
66
|
3322
|
|
|
|
|
3518
|
my $count = 0; |
67
|
3322
|
100
|
|
|
|
6494
|
($func, $count) = @$func if ref $func eq "ARRAY"; |
68
|
3322
|
50
|
|
|
|
5659
|
if (ref $func) { |
69
|
0
|
|
|
|
|
0
|
warn "Invalid record of type ", ref $func, " requested"; |
70
|
0
|
|
|
|
|
0
|
return undef; |
71
|
|
|
|
|
|
|
} |
72
|
3322
|
|
|
|
|
4404
|
my $record = $Gedcom::Funcs{lc $func}; |
73
|
3322
|
50
|
|
|
|
4278
|
unless ($record) { |
74
|
0
|
0
|
|
|
|
0
|
warn $func |
|
|
0
|
|
|
|
|
|
75
|
|
|
|
|
|
|
? "Non standard record of type $func requested" |
76
|
|
|
|
|
|
|
: "Record type not specified" |
77
|
|
|
|
|
|
|
unless $func =~ /^_/; |
78
|
0
|
|
|
|
|
0
|
$record = $func; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
3322
|
|
|
|
|
4922
|
@records = map { $_->tag_record($record, $count) } @records; |
|
3315
|
|
|
|
|
5333
|
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# fams and famc need to be resolved |
84
|
3322
|
50
|
33
|
|
|
9665
|
@records = map { $self->resolve($_->{value}) } @records |
|
0
|
|
|
|
|
0
|
|
85
|
|
|
|
|
|
|
if $record eq "FAMS" || $record eq "FAMC"; |
86
|
|
|
|
|
|
|
} |
87
|
3219
|
100
|
|
|
|
5759
|
wantarray ? @records : $records[0] |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub get_value { |
91
|
103
|
|
|
103
|
1
|
33404
|
my $self = shift; |
92
|
103
|
100
|
|
|
|
213
|
if (wantarray) { |
93
|
96
|
50
|
|
|
|
183
|
return map { my $v = $_->full_value; defined $v and length $v ? $v : () } |
|
82
|
50
|
|
|
|
154
|
|
|
82
|
|
|
|
|
339
|
|
94
|
|
|
|
|
|
|
$self->record(@_); |
95
|
|
|
|
|
|
|
} else { |
96
|
7
|
|
|
|
|
29
|
my $record = $self->record(@_); |
97
|
7
|
|
66
|
|
|
38
|
return $record && $record->full_value; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub tag_value { |
102
|
62489
|
|
|
62489
|
0
|
63467
|
my $self = shift; |
103
|
62489
|
100
|
|
|
|
73337
|
if (wantarray) { |
104
|
52414
|
50
|
|
|
|
80867
|
return map { my $v = $_->full_value; defined $v and length $v ? $v : () } |
|
53259
|
50
|
|
|
|
81953
|
|
|
53259
|
|
|
|
|
179447
|
|
105
|
|
|
|
|
|
|
$self->tag_record(@_); |
106
|
|
|
|
|
|
|
} else { |
107
|
10075
|
|
|
|
|
15343
|
my $record = $self->tag_record(@_); |
108
|
10075
|
|
33
|
|
|
20304
|
return $record && $record->full_value; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub add_record { |
113
|
80
|
|
|
80
|
0
|
90
|
my $self = shift; |
114
|
80
|
|
|
|
|
173
|
my (%args) = @_; |
115
|
|
|
|
|
|
|
|
116
|
80
|
50
|
|
|
|
145
|
die "No tag specified" unless defined $args{tag}; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
my $record = Gedcom::Record->new( |
119
|
|
|
|
|
|
|
gedcom => $self->{gedcom}, |
120
|
|
|
|
|
|
|
callback => $self->{callback}, |
121
|
|
|
|
|
|
|
tag => $args{tag}, |
122
|
80
|
|
|
|
|
178
|
); |
123
|
|
|
|
|
|
|
|
124
|
80
|
50
|
|
|
|
215
|
if (!defined $self->{grammar}) { |
|
|
50
|
|
|
|
|
|
125
|
0
|
|
|
|
|
0
|
warn "$self->{tag} has no grammar\n"; |
126
|
|
|
|
|
|
|
} elsif (my @g = $self->{grammar}->item($args{tag})) { |
127
|
|
|
|
|
|
|
# use DDS; print Dump \@g; |
128
|
80
|
|
|
|
|
168
|
my $grammar = $g[0]; |
129
|
80
|
|
|
|
|
114
|
for my $g (@g) { |
130
|
|
|
|
|
|
|
# print "testing $args{tag} ", $args{val} // "undef", " against ", |
131
|
|
|
|
|
|
|
# $g->{value} // "undef", "\n"; |
132
|
82
|
100
|
|
|
|
124
|
if ($args{tag} eq "NOTE") { |
133
|
6
|
100
|
66
|
|
|
44
|
if (( defined $args{xref} && $g->{value} =~ /xref/i) || |
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
134
|
|
|
|
|
|
|
(!defined $args{xref} && $g->{value} !~ /xref/i)) { |
135
|
|
|
|
|
|
|
# print "note match\n"; |
136
|
5
|
|
|
|
|
6
|
$grammar = $g; |
137
|
5
|
|
|
|
|
9
|
last; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
} else { |
140
|
76
|
100
|
100
|
|
|
260
|
if (( defined $args{val} && $g->{value}) || |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
141
|
|
|
|
|
|
|
(!defined $args{val} && !$g->{value})) { |
142
|
|
|
|
|
|
|
# print "match\n"; |
143
|
67
|
|
|
|
|
75
|
$grammar = $g; |
144
|
67
|
|
|
|
|
88
|
last; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
} |
148
|
80
|
|
|
|
|
158
|
$self->parse($record, $grammar); |
149
|
|
|
|
|
|
|
} else { |
150
|
0
|
|
|
|
|
0
|
warn "$args{tag} is not a sub-item of $self->{tag}\n"; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
80
|
|
|
|
|
85
|
push @{$self->{items}}, $record; |
|
80
|
|
|
|
|
109
|
|
154
|
|
|
|
|
|
|
|
155
|
80
|
|
|
|
|
197
|
$record |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub add { |
159
|
62
|
|
|
62
|
1
|
92
|
my $self = shift; |
160
|
62
|
|
|
|
|
81
|
my ($xref, $val); |
161
|
62
|
100
|
66
|
|
|
189
|
if (@_ > 1 && ref $_[-1] ne "ARRAY") { |
162
|
59
|
|
|
|
|
78
|
$val = pop; |
163
|
59
|
100
|
|
|
|
199
|
if (UNIVERSAL::isa($val, "Gedcom::Record")) { |
164
|
6
|
|
|
|
|
6
|
$xref = $val; |
165
|
6
|
|
|
|
|
7
|
$val = undef; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
62
|
100
|
|
|
|
95
|
my @funcs = map { ref() ? $_ : split } @_; |
|
64
|
|
|
|
|
188
|
|
170
|
62
|
100
|
|
|
|
174
|
$funcs[-1] = [$funcs[-1], 0] unless ref $funcs[-1]; |
171
|
62
|
|
|
|
|
74
|
push @{$funcs[-1]}, { xref => $xref, val => $val }; |
|
62
|
|
|
|
|
162
|
|
172
|
62
|
|
|
|
|
124
|
my $record = $self->get_and_create(@funcs); |
173
|
|
|
|
|
|
|
|
174
|
62
|
100
|
|
|
|
93
|
if (defined $xref) { |
175
|
6
|
|
|
|
|
11
|
$record->{value} = $xref->{xref}; |
176
|
6
|
|
|
|
|
12
|
$self->{gedcom}{xrefs}{$xref->{xref}} = $xref; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
62
|
100
|
|
|
|
87
|
if (defined $val) { |
180
|
53
|
|
|
|
|
80
|
$record->{value} = $val; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
$record |
184
|
62
|
|
|
|
|
216
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub set { |
187
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
188
|
1
|
|
|
|
|
2
|
my $val = pop; |
189
|
|
|
|
|
|
|
|
190
|
1
|
50
|
|
|
|
3
|
my @funcs = map { ref() ? $_ : split } @_; |
|
1
|
|
|
|
|
6
|
|
191
|
1
|
|
|
|
|
3
|
my $r = $self->get_and_create(@funcs); |
192
|
|
|
|
|
|
|
|
193
|
1
|
50
|
|
|
|
6
|
if (UNIVERSAL::isa($val, "Gedcom::Record")) { |
194
|
0
|
|
|
|
|
0
|
$r->{value} = $val->{xref}; |
195
|
0
|
|
|
|
|
0
|
$self->{gedcom}{xrefs}{$val->{xref}} = $val; |
196
|
|
|
|
|
|
|
} else { |
197
|
1
|
|
|
|
|
3
|
$r->{value} = $val; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
1
|
|
|
|
|
13
|
$r |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub get_and_create { |
204
|
63
|
|
|
63
|
0
|
69
|
my $self = shift; |
205
|
63
|
|
|
|
|
84
|
my @funcs = @_; |
206
|
|
|
|
|
|
|
# use DDS; print "get_and_create: " , Dump \@funcs; |
207
|
|
|
|
|
|
|
|
208
|
63
|
|
|
|
|
68
|
my $rec = $self; |
209
|
63
|
|
|
|
|
113
|
for my $f (0 .. $#funcs) { |
210
|
72
|
|
|
|
|
120
|
my ($func, $count, $args) = ($funcs[$f], 1); |
211
|
72
|
50
|
|
|
|
119
|
$args = {} unless defined $args; |
212
|
72
|
100
|
|
|
|
193
|
($func, $count, $args) = @$func if ref $func eq "ARRAY"; |
213
|
72
|
|
|
|
|
77
|
$count--; |
214
|
|
|
|
|
|
|
|
215
|
72
|
50
|
|
|
|
113
|
if (ref $func) { |
216
|
0
|
|
|
|
|
0
|
warn "Invalid record of type ", ref $func, " requested"; |
217
|
0
|
|
|
|
|
0
|
return undef; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
72
|
|
|
|
|
130
|
my $record = $Gedcom::Funcs{lc $func}; |
221
|
72
|
50
|
|
|
|
99
|
unless ($record) { |
222
|
0
|
0
|
|
|
|
0
|
warn $func |
|
|
0
|
|
|
|
|
|
223
|
|
|
|
|
|
|
? "Non standard record of type $func requested" |
224
|
|
|
|
|
|
|
: "Record type not specified" |
225
|
|
|
|
|
|
|
unless $func =~ /^_/; |
226
|
0
|
|
|
|
|
0
|
$record = $func; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# print "$func [$count] - $record\n"; |
230
|
|
|
|
|
|
|
|
231
|
72
|
|
|
|
|
163
|
my @records = $rec->tag_record($record); |
232
|
|
|
|
|
|
|
|
233
|
72
|
100
|
|
|
|
116
|
if ($count < 0) { |
|
|
100
|
|
|
|
|
|
234
|
61
|
|
|
|
|
161
|
$rec = $rec->add_record(tag => $record, %$args); |
235
|
|
|
|
|
|
|
} elsif ($#records < $count) { |
236
|
7
|
|
|
|
|
77
|
my $new; |
237
|
|
|
|
|
|
|
$new = $rec->add_record(tag => $record, %$args) |
238
|
7
|
|
|
|
|
56
|
for (0 .. @records - $count); |
239
|
7
|
|
|
|
|
15
|
$rec = $new; |
240
|
|
|
|
|
|
|
} else { |
241
|
4
|
|
|
|
|
9
|
$rec = $records[$count]; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
$rec |
246
|
63
|
|
|
|
|
82
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub parse { |
249
|
|
|
|
|
|
|
# print "parsing\n"; |
250
|
6531
|
|
|
6531
|
1
|
6640
|
my $self = shift; |
251
|
6531
|
|
|
|
|
8471
|
my ($record, $grammar, $test) = @_; |
252
|
6531
|
|
50
|
|
|
15996
|
$test ||= 0; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# print "checking "; $record->print(); |
255
|
|
|
|
|
|
|
# print "against "; $grammar->print(); |
256
|
|
|
|
|
|
|
# print "test is $test\n"; |
257
|
|
|
|
|
|
|
|
258
|
6531
|
|
|
|
|
7697
|
my $t = $record->{tag}; |
259
|
6531
|
|
|
|
|
6982
|
my $g = $grammar->{tag}; |
260
|
6531
|
50
|
33
|
|
|
13842
|
die "Can't match $t with $g" if $t && $t ne $g; # internal error |
261
|
|
|
|
|
|
|
|
262
|
6531
|
|
|
|
|
7819
|
$record->{grammar} = $grammar; |
263
|
6531
|
|
|
|
|
8297
|
my $class = $record->{gedcom}{types}{$t}; |
264
|
6531
|
100
|
|
|
|
9630
|
bless $record, "Gedcom::$class" if $class; |
265
|
|
|
|
|
|
|
|
266
|
6531
|
|
|
|
|
6309
|
my $match = 1; |
267
|
|
|
|
|
|
|
|
268
|
6531
|
|
|
|
|
6233
|
for my $r (@{$record->{items}}) { |
|
6531
|
|
|
|
|
9593
|
|
269
|
5580
|
|
|
|
|
6627
|
my $tag = $r->{tag}; |
270
|
5580
|
|
|
|
|
5233
|
my @i; |
271
|
|
|
|
|
|
|
# print "- valid sub-items of $t: @{[keys %{$grammar->valid_items}]}\n"; |
272
|
5580
|
|
|
|
|
8448
|
for my $i ($grammar->item($tag)) { |
273
|
|
|
|
|
|
|
# Try to get rid of matches we don't want because they only match |
274
|
|
|
|
|
|
|
# in name. |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# Check that the level is appropriate. |
277
|
|
|
|
|
|
|
# print " - ", $i->level, "|", $r->level, "\n"; |
278
|
5590
|
50
|
33
|
|
|
8811
|
next unless $i->level =~ /^[+0]/ || $i->level == $r->level; |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# Check we have a pointer iff we need one. |
281
|
|
|
|
|
|
|
# print " + ", $i->value, "|", $r->value, "|", $r->pointer, "\n"; |
282
|
5590
|
100
|
100
|
|
|
9258
|
next if $i->value && ($i->value =~ /^pointer || 0)); |
|
|
|
100
|
|
|
|
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# print "pushing\n"; |
285
|
5575
|
|
|
|
|
9184
|
push @i, $i; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# print "valid sub-items of $t: @{[keys %{$grammar->valid_items}]}\n"; |
289
|
|
|
|
|
|
|
# print "<$tag> => <@i>\n"; |
290
|
|
|
|
|
|
|
|
291
|
5580
|
100
|
|
|
|
8122
|
unless (@i) { |
292
|
|
|
|
|
|
|
# unless $tag eq "CONT" || $tag eq "CONC" |
293
|
|
|
|
|
|
|
# || substr($tag, 0, 1) eq "_"; |
294
|
|
|
|
|
|
|
# TODO - should CONT and CONC be allowed anywhere? |
295
|
5
|
50
|
|
|
|
26
|
unless (substr($tag, 0, 1) eq "_") { |
296
|
|
|
|
|
|
|
warn "$self->{file}:$r->{line}: $tag is not a sub-item of $t\n", |
297
|
|
|
|
|
|
|
"Valid sub-items are ", |
298
|
0
|
0
|
|
|
|
0
|
join(", ", sort keys %{$grammar->{_valid_items}}), "\n" |
|
0
|
|
|
|
|
0
|
|
299
|
|
|
|
|
|
|
unless $test; |
300
|
0
|
|
|
|
|
0
|
$match = 0; |
301
|
0
|
|
|
|
|
0
|
next; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# print "$self->{file}:$r->{line}: Ambiguous tag $tag as sub-item of $t, ", |
306
|
|
|
|
|
|
|
# "found ", scalar @i, " matches\n" if @i > 1; |
307
|
5580
|
|
|
|
|
5815
|
my $m = 0; |
308
|
5580
|
|
|
|
|
6186
|
for my $i (@i) { |
309
|
5575
|
50
|
|
|
|
8076
|
last if $m = $self->parse($r, $i, @i > 1); |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
5580
|
50
|
33
|
|
|
10362
|
if (@i > 1 && !$m) { |
313
|
|
|
|
|
|
|
# TODO - I'm not even sure if this can happen. |
314
|
0
|
|
|
|
|
0
|
warn "$self->{file}:$r->{line}:" , |
315
|
|
|
|
|
|
|
"Ambiguous tag $tag as sub-item of $t, ", |
316
|
|
|
|
|
|
|
"found ", scalar @i, " matches, all of which have errors. ", |
317
|
|
|
|
|
|
|
"Reporting errors from last match.\n"; |
318
|
0
|
|
|
|
|
0
|
$self->parse($r, $i[-1]); |
319
|
0
|
|
|
|
|
0
|
$match = 0; |
320
|
|
|
|
|
|
|
# TODO - count the errors in each match and use the best. |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
# print "parsed $match\n"; |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
$match |
326
|
6531
|
|
|
|
|
11518
|
} |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
sub collect_xrefs { |
329
|
21861
|
|
|
21861
|
1
|
20917
|
my $self = shift; |
330
|
21861
|
|
|
|
|
22630
|
my ($callback) = @_; |
331
|
21861
|
100
|
|
|
|
34266
|
$self->{gedcom}{xrefs}{$self->{xref}} = $self if defined $self->{xref}; |
332
|
21861
|
|
|
|
|
20214
|
$_->collect_xrefs($callback) for @{$self->{items}}; |
|
21861
|
|
|
|
|
35778
|
|
333
|
21861
|
|
|
|
|
32366
|
$self |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub resolve_xref { |
337
|
5287
|
|
|
5287
|
1
|
9444
|
shift->{gedcom}->resolve_xref(@_); |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub resolve { |
341
|
15632
|
|
|
15632
|
1
|
19560
|
my $self = shift; |
342
|
|
|
|
|
|
|
my @x = map { |
343
|
15632
|
|
|
|
|
18251
|
ref($_) |
344
|
|
|
|
|
|
|
? $_ |
345
|
14083
|
100
|
|
|
|
25297
|
: do { my $x = $self->{gedcom}->resolve_xref($_); defined $x ? $x : () } |
|
1387
|
100
|
|
|
|
2498
|
|
|
1387
|
|
|
|
|
2569
|
|
346
|
|
|
|
|
|
|
} @_; |
347
|
15632
|
100
|
|
|
|
30620
|
wantarray ? @x : $x[0] |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub resolve_xrefs { |
351
|
22803
|
|
|
22803
|
1
|
24511
|
my $self = shift; |
352
|
22803
|
|
|
|
|
26734
|
my ($callback) = @_; |
353
|
22803
|
100
|
|
|
|
45285
|
if (my $xref = $self->{gedcom}->resolve_xref($self->{value})) { |
354
|
2572
|
|
|
|
|
3014
|
$self->{value} = $xref; |
355
|
|
|
|
|
|
|
} |
356
|
22803
|
|
|
|
|
25041
|
$_->resolve_xrefs($callback) for @{$self->_items}; |
|
22803
|
|
|
|
|
38941
|
|
357
|
22803
|
|
|
|
|
42237
|
$self |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub unresolve_xrefs { |
361
|
15156
|
|
|
15156
|
1
|
15024
|
my $self = shift;; |
362
|
15156
|
|
|
|
|
16305
|
my ($callback) = @_; |
363
|
|
|
|
|
|
|
$self->{value} = $self->{value}{xref} |
364
|
|
|
|
|
|
|
if defined $self->{value} |
365
|
|
|
|
|
|
|
and UNIVERSAL::isa $self->{value}, "Gedcom::Record" |
366
|
15156
|
50
|
100
|
|
|
49910
|
and exists $self->{value}{xref}; |
|
|
|
66
|
|
|
|
|
367
|
15156
|
|
|
|
|
14502
|
$_->unresolve_xrefs($callback) for @{$self->_items}; |
|
15156
|
|
|
|
|
19435
|
|
368
|
15156
|
|
|
|
|
22935
|
$self |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
my $D = 0; # turn on debug output |
372
|
|
|
|
|
|
|
my $I = -1; # indent for debug output |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
sub validate_syntax { |
375
|
69989
|
|
|
69989
|
0
|
71866
|
my $self = shift; |
376
|
69989
|
100
|
|
|
|
110122
|
return 1 unless exists $self->{grammar}; |
377
|
53489
|
|
|
|
|
51076
|
my $ok = 1; |
378
|
|
|
|
|
|
|
$self->{gedcom}{validate_callback}->($self) |
379
|
53489
|
50
|
|
|
|
103910
|
if defined $self->{gedcom}{validate_callback}; |
380
|
53489
|
|
|
|
|
61943
|
my $grammar = $self->{grammar}; |
381
|
53489
|
|
|
|
|
49103
|
$I++; |
382
|
|
|
|
|
|
|
print " " x $I . "validate_syntax(" . |
383
|
53489
|
0
|
|
|
|
63007
|
(defined $grammar->{tag} ? $grammar->{tag} : "") . ")\n" if $D; |
|
|
50
|
|
|
|
|
|
384
|
53489
|
|
|
|
|
65581
|
my $file = $self->{gedcom}{record}{file}; |
385
|
|
|
|
|
|
|
my $here = "$file:$self->{line}: $self->{tag}" . |
386
|
53489
|
100
|
|
|
|
115732
|
(defined $self->{xref} ? " $self->{xref}" : ""); |
387
|
|
|
|
|
|
|
# print "$self->{line}: "; $self->print; |
388
|
|
|
|
|
|
|
$ok = 0, warn "$here: $self->{tag} Can't contain a value ($self->{value})\n" |
389
|
|
|
|
|
|
|
if defined $self->{value} && length $self->{value} && |
390
|
53489
|
100
|
100
|
|
|
176024
|
!defined $grammar->{value}; |
|
|
|
100
|
|
|
|
|
391
|
53489
|
|
|
|
|
56555
|
my %counts; |
392
|
53489
|
|
|
|
|
50498
|
for my $record (@{$self->_items}) { |
|
53489
|
|
|
|
|
82184
|
|
393
|
69928
|
50
|
|
|
|
89333
|
print " " x $I . "level $record->{level} on $self->{level}\n" if $D; |
394
|
|
|
|
|
|
|
$ok = 0, |
395
|
|
|
|
|
|
|
warn "$here: Can't add level $record->{level} to $self->{level}\n" |
396
|
69928
|
50
|
|
|
|
126770
|
if $record->{level} > $self->{level} + 1; |
397
|
69928
|
|
|
|
|
113814
|
$counts{$record->{tag}}++; |
398
|
69928
|
100
|
|
|
|
94643
|
$ok = 0 unless $record->validate_syntax; |
399
|
|
|
|
|
|
|
} |
400
|
53489
|
|
|
|
|
86972
|
my $valid_items = $grammar->valid_items; |
401
|
53489
|
|
|
|
|
212507
|
for my $tag (sort keys %$valid_items) { |
402
|
563458
|
|
|
|
|
544313
|
for my $g (@{$valid_items->{$tag}}) { |
|
563458
|
|
|
|
|
702825
|
|
403
|
637008
|
|
|
|
|
653679
|
my $min = $g->{min}; |
404
|
637008
|
|
|
|
|
639197
|
my $max = $g->{max}; |
405
|
637008
|
|
100
|
|
|
1130659
|
my $matches = delete $counts{$tag} || 0; |
406
|
637008
|
100
|
|
|
|
1076357
|
my $msg = "$here has $matches $tag" . ($matches == 1 ? "" : "s"); |
407
|
637008
|
50
|
|
|
|
821449
|
print " " x $I . "$msg - min is $min max is $max\n" if $D; |
408
|
637008
|
50
|
|
|
|
777558
|
$ok = 0, warn "$msg - minimum is $min\n" if $matches < $min; |
409
|
637008
|
100
|
100
|
|
|
1102814
|
$ok = 0, warn "$msg - maximum is $max\n" if $matches > $max && $max; |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} |
412
|
53489
|
|
|
|
|
81106
|
for my $tag (keys %counts) { |
413
|
60
|
|
|
|
|
335
|
for my $c ($self->tag_record($tag)) { |
414
|
60
|
50
|
|
|
|
261
|
$ok = 0, |
415
|
|
|
|
|
|
|
warn "$file:$c->{line}: $tag is not a sub-item of $self->{tag}\n" |
416
|
|
|
|
|
|
|
unless substr($tag, 0, 1) eq "_"; |
417
|
|
|
|
|
|
|
# unless $tag eq "CONT" || $tag eq "CONC" || substr($tag, 0, 1) eq "_"; |
418
|
|
|
|
|
|
|
# TODO - should CONT and CONC be allowed anywhere? |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
} |
421
|
53489
|
|
|
|
|
50773
|
$I--; |
422
|
53489
|
|
|
|
|
106439
|
$ok; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
my $Check = { |
426
|
|
|
|
|
|
|
INDI => { |
427
|
|
|
|
|
|
|
FAMS => [ "HUSB", "WIFE" ], |
428
|
|
|
|
|
|
|
FAMC => [ "CHIL" ] |
429
|
|
|
|
|
|
|
}, |
430
|
|
|
|
|
|
|
FAM => { |
431
|
|
|
|
|
|
|
HUSB => [ "FAMS" ], |
432
|
|
|
|
|
|
|
WIFE => [ "FAMS" ], |
433
|
|
|
|
|
|
|
CHIL => [ "FAMC" ], |
434
|
|
|
|
|
|
|
}, |
435
|
|
|
|
|
|
|
}; |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
sub validate_semantics { |
438
|
8770
|
|
|
8770
|
1
|
9317
|
my $self = shift; |
439
|
8770
|
100
|
100
|
|
|
21112
|
return 1 unless $self->{tag} eq "INDI" || $self->{tag} eq "FAM"; |
440
|
|
|
|
|
|
|
# print "validating: "; $self->print; print $self->summary, "\n"; |
441
|
8405
|
|
|
|
|
8384
|
my $ok = 1; |
442
|
8405
|
|
|
|
|
10818
|
my $xrefs = $self->{gedcom}{xrefs}; |
443
|
8405
|
|
|
|
|
11645
|
my $chk = $Check->{$self->{tag}}; |
444
|
8405
|
|
|
|
|
14148
|
for my $f (keys %$chk) { |
445
|
19630
|
|
|
|
|
21678
|
my $found = 1; |
446
|
|
|
|
|
|
|
RECORD: |
447
|
19630
|
|
|
|
|
27126
|
for my $record ($self->tag_value($f)) { |
448
|
15244
|
|
|
|
|
16736
|
$found = 0; |
449
|
15244
|
100
|
|
|
|
24579
|
$record = $xrefs->{$record} unless ref $record; |
450
|
15244
|
100
|
|
|
|
21886
|
if ($record) { |
451
|
15240
|
|
|
|
|
15270
|
for my $back (@{$chk->{$f}}) { |
|
15240
|
|
|
|
|
21303
|
|
452
|
|
|
|
|
|
|
# print "back $back\n"; |
453
|
17160
|
|
|
|
|
23795
|
for my $i ($record->tag_value($back)) { |
454
|
|
|
|
|
|
|
# print "record is $i\n"; |
455
|
20400
|
100
|
|
|
|
33909
|
$i = $xrefs->{$i} unless ref $i; |
456
|
20400
|
100
|
66
|
|
|
51264
|
if ($i && $i->{xref} eq $self->{xref}) { |
457
|
15240
|
|
|
|
|
15626
|
$found = 1; |
458
|
|
|
|
|
|
|
# print "found...\n"; |
459
|
15240
|
|
|
|
|
24071
|
next RECORD; |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
} |
463
|
0
|
0
|
|
|
|
0
|
unless ($found) { |
464
|
|
|
|
|
|
|
# TODO - use the line of the offending record |
465
|
0
|
|
|
|
|
0
|
$ok = 0; |
466
|
0
|
|
|
|
|
0
|
my $file = $self->{gedcom}{record}{file}; |
467
|
|
|
|
|
|
|
warn "$file:$self->{line}: $f $record->{xref} " . |
468
|
|
|
|
|
|
|
"does not reference $self->{tag} $self->{xref}. " . |
469
|
|
|
|
|
|
|
"Add the line:\n" . |
470
|
|
|
|
|
|
|
"$file:" . ($record->{line} + 1) . ": 1 " . |
471
|
0
|
|
|
|
|
0
|
join("or ", @{$chk->{$f}}) . " $self->{xref}\n"; |
|
0
|
|
|
|
|
0
|
|
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
} |
476
|
8405
|
|
|
|
|
19587
|
$ok; |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
sub normalise_dates { |
480
|
7578
|
|
|
7578
|
1
|
8877
|
my $self = shift; |
481
|
7578
|
50
|
|
|
|
10680
|
unless ($INC{"Date/Manip.pm"}) { |
482
|
0
|
|
|
|
|
0
|
warn "Date::Manip.pm is required to use normalise_dates()"; |
483
|
0
|
|
|
|
|
0
|
return; |
484
|
|
|
|
|
|
|
} |
485
|
7578
|
50
|
33
|
|
|
18340
|
if( eval { Date::Manip->VERSION( 6 ) } && |
|
7578
|
|
|
|
|
45993
|
|
486
|
7578
|
|
|
|
|
54286
|
!eval { Date::Manip->VERSION( 6.13 ) } ) { |
487
|
0
|
|
|
|
|
0
|
warn "Unable to normalize dates with this version of Date::Manip. " . |
488
|
|
|
|
|
|
|
"Please upgrade to version 6.13."; |
489
|
|
|
|
|
|
|
return |
490
|
0
|
|
|
|
|
0
|
} |
491
|
7578
|
|
100
|
|
|
14136
|
my $format = shift || "%A, %E %B %Y"; |
492
|
7578
|
100
|
66
|
|
|
25036
|
if (defined $self->{tag} && $self->{tag} =~ /^date$/i) { |
493
|
906
|
50
|
33
|
|
|
3712
|
if (defined $self->{value} && $self->{value}) { |
494
|
|
|
|
|
|
|
# print "date was $self->{value}\n"; |
495
|
906
|
|
|
|
|
2249
|
my @dates = split / or /, $self->{value}; |
496
|
906
|
|
|
|
|
1346
|
for my $dt (@dates) { |
497
|
|
|
|
|
|
|
# Don't change the date if it looks like 'AFT 1989'. |
498
|
|
|
|
|
|
|
# AFT means AFTER and ParseDate returns the current date and the tests |
499
|
|
|
|
|
|
|
# are failing. |
500
|
|
|
|
|
|
|
# Current date can symbolize such an "after" date, but can also |
501
|
|
|
|
|
|
|
# symbolize a very specific point in time and that could also confuse |
502
|
|
|
|
|
|
|
# the user. |
503
|
906
|
100
|
|
|
|
1656
|
next if $dt =~ /^AFT/; |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
# Don't change the date if it is just < 7 digits. |
506
|
900
|
100
|
66
|
|
|
3242
|
if ($dt !~ /^\s*(\d+)\s*$/ || length $1 > 6) { |
507
|
642
|
|
|
|
|
1516
|
my $date = ParseDate($dt); |
508
|
642
|
|
|
|
|
947602
|
my $d = UnixDate($date, $format); |
509
|
642
|
100
|
|
|
|
340920
|
$dt = $d if $d; |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
} |
512
|
906
|
|
|
|
|
2133
|
$self->{value} = join " or ", @dates; |
513
|
|
|
|
|
|
|
# print "date is $self->{value}\n"; |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
} |
516
|
7578
|
|
|
|
|
8423
|
$_->normalise_dates($format) for @{$self->_items}; |
|
7578
|
|
|
|
|
12666
|
|
517
|
7578
|
100
|
|
|
|
12839
|
$self->delete_items if $self->level > 1; |
518
|
|
|
|
|
|
|
} |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
sub renumber { |
521
|
13600
|
|
|
13600
|
1
|
13840
|
my $self = shift; |
522
|
13600
|
|
|
|
|
14701
|
my ($args, $recurse) = @_; |
523
|
|
|
|
|
|
|
# TODO - add the xref if there is supposed to be one |
524
|
13600
|
100
|
100
|
|
|
31585
|
return if exists $self->{recursed} or not defined $self->{xref}; |
525
|
|
|
|
|
|
|
# we can't actually change the xrefs until the end |
526
|
4496
|
100
|
|
|
|
8169
|
my $x = $self->{tag} eq "SUBM" ? "SUBM" : substr $self->{tag}, 0, 1; |
527
|
|
|
|
|
|
|
$self->{new_xref} = $x . ++$args->{$self->{tag}} |
528
|
4496
|
100
|
|
|
|
8222
|
unless exists $self->{new_xref}; |
529
|
4496
|
100
|
66
|
|
|
10007
|
return unless $recurse and not exists $self->{recursed}; |
530
|
1736
|
|
|
|
|
2247
|
$self->{recursed} = 1; |
531
|
1736
|
100
|
|
|
|
4444
|
if ($self->{tag} eq "INDI") { |
532
|
1121
|
|
|
|
|
1470
|
my @r = map { $self->$_() } |
|
6726
|
|
|
|
|
14161
|
|
533
|
|
|
|
|
|
|
qw( fams famc spouse children parents siblings ); |
534
|
1121
|
|
|
|
|
2569
|
$_->renumber($args, 0) for @r; |
535
|
1121
|
|
|
|
|
2043
|
$_->renumber($args, 1) for @r; |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
sub child_value { |
540
|
|
|
|
|
|
|
# NOTE - This function is deprecated - use tag_value instead |
541
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
542
|
0
|
|
|
|
|
|
$self->tag_value(@_) |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
sub child_values { |
546
|
|
|
|
|
|
|
# NOTE - This function is deprecated - use tag_value instead |
547
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
548
|
0
|
|
|
|
|
|
$self->tag_value(@_) |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
sub compare { |
552
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
553
|
0
|
|
|
|
|
|
my ($r) = @_; |
554
|
0
|
|
|
|
|
|
Gedcom::Comparison->new($self, $r) |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
sub summary { |
558
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
559
|
0
|
|
|
|
|
|
my $s = ""; |
560
|
0
|
|
|
|
|
|
$s .= sprintf "%-5s", $self->{xref}; |
561
|
0
|
|
|
|
|
|
my $r = $self->tag_record("NAME"); |
562
|
0
|
0
|
|
|
|
|
$s .= sprintf " %-40s", $r ? $r->{value} : ""; |
563
|
0
|
|
|
|
|
|
$r = $self->tag_record("SEX"); |
564
|
0
|
0
|
|
|
|
|
$s .= sprintf " %1s", $r ? $r->{value} : ""; |
565
|
0
|
|
|
|
|
|
my $d = ""; |
566
|
0
|
0
|
0
|
|
|
|
if ($r = $self->tag_record("BIRT") and my $date = $r->tag_record("DATE")) { |
567
|
0
|
|
|
|
|
|
$d = $date->{value}; |
568
|
|
|
|
|
|
|
} |
569
|
0
|
|
|
|
|
|
$s .= sprintf " %16s", $d; |
570
|
0
|
|
|
|
|
|
$s; |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
1; |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
__END__ |