File Coverage

blib/lib/xDT/Parser.pm
Criterion Covered Total %
statement 20 52 38.4
branch 0 18 0.0
condition 0 3 0.0
subroutine 7 12 58.3
pod 4 4 100.0
total 31 89 34.8


line stmt bran cond sub pod time code
1             package xDT::Parser;
2              
3 1     1   69267 use v5.10;
  1         4  
4 1     1   656 use Moose;
  1         490411  
  1         8  
5 1     1   8394 use FileHandle;
  1         10775  
  1         9  
6              
7 1     1   987 use xDT::Record;
  1         4  
  1         55  
8 1     1   14 use xDT::RecordType;
  1         2  
  1         24  
9 1     1   684 use xDT::Object;
  1         4  
  1         516  
10              
11             =head1 NAME
12              
13             xDT::Parser - A Parser for xDT files.
14              
15             =head1 VERSION
16              
17             Version 1.04
18              
19             =cut
20              
21             our $VERSION = '1.04';
22              
23              
24             =head1 SYNOPSIS
25              
26             Can be used to open xdt files and strings, and to iterate over contained objects.
27              
28             use xDT::Parser;
29              
30             my $parser = xDT::Parser->new();
31             # or
32             my $parser = xDT::Parser->new(record_type_config => $config);
33             # or
34             my $parser = xDT::Parser->new(
35             record_type_config => xDT::Parser::build_config_from_xml($xml_file)
36             );
37             # or
38             my $parser = xDT::Parser->new(
39             record_type_config => JSON::Parser::read_json($json_file)
40             );
41              
42             # A record type configuration can be provided via xml file or arrayref and can be used to add
43             # metadata (like accessor string or labels) to each record type.
44              
45             $parser->open(file => $xdt_file); # read from file
46             # or
47             $parser->open(string => $xdt_string); # read from string
48              
49             while (my $object = $parser->next_object) { # iterate xdt objects
50             # ...
51             }
52              
53             $parser->close(); # close the file handle
54              
55             =head1 ATTRIBUTES
56              
57             =head2 fh
58              
59             FileHandle to the currently open file.
60              
61             =cut
62              
63             has 'fh' => (
64             is => 'rw',
65             isa => 'FileHandle',
66             documentation => q{The filehandle the parser will use to read xDT data.},
67             );
68              
69             =head2 record_type_config
70              
71             The C<RecordType> configurations.
72              
73             e.g.:
74              
75             [{
76             "id": "0201",
77             "length": "9",
78             "type": "num",
79             "accessor": "bsnr",
80             "labels": {
81             "en": "BSNR",
82             "de": "BSNR"
83             }
84             }]
85              
86             =cut
87              
88             has 'record_type_config' => (
89             is => 'rw',
90             isa => 'ArrayRef',
91             documentation => q{Contains configurations for record types.},
92             );
93              
94              
95             around BUILDARGS => sub {
96             my $orig = shift;
97             my $class = shift;
98              
99             if (@_ == 1) {
100             return $class->$orig(record_type_config => $_[0]);
101             } else {
102             my %params = @_;
103             return $class->$orig(\%params);
104             }
105             };
106              
107             =head1 SUBROUTINES/METHODS
108              
109             =head2 open
110              
111             $parser->open(file => 'example.gdt');
112             $parser->open(string => $xdt_string);
113              
114             Open a file or string with the parser.
115             If both file and string are given, the string will be ignored.
116             More information about the file format can be found at L<http://search.cpan.org/dist/xDT-RecordType/>.
117              
118             =cut
119              
120             sub open {
121 0     0 1   my ($self, %args) = @_;
122              
123 0           my $file = $args{file};
124 0           my $string = $args{string};
125 0           my $fh;
126              
127 0 0 0       die 'Error: No file or string argument given to parse xDT.'
128             unless (defined $file or defined $string);
129              
130 0 0         if (defined $file) {
131 0 0         die "Error: Provided file '$file' does not exist or is not readable."
132             unless (-f $file);
133              
134 0 0         $fh = FileHandle->new($file, 'r')
135             or die "Error: Could not open file handle for '$file'.";
136             } else {
137 0 0         $fh = FileHandle->new(\$string, 'r')
138             or die 'Error: Could not open file handle for provided string.';
139             }
140              
141 0           $self->fh($fh);
142             }
143              
144             =head2 close
145              
146             Closes the parsers filehandle
147              
148             =cut
149              
150             sub close {
151 0     0 1   my $self = shift;
152              
153 0           close $self->fh;
154             }
155              
156             =head2 next_object
157              
158             Returns the next object from xDT.
159              
160             =cut
161              
162             sub next_object {
163 0     0 1   my $self = shift;
164 0           my @records;
165              
166 0           while (my $record = $self->_next()) {
167 0 0         last if ($record->is_object_end);
168 0           push @records, $record;
169             }
170              
171 0 0         return undef unless (scalar @records);
172              
173 0           my $object = xDT::Object->new();
174 0           foreach my $record (@records) {
175 0           $object->add_record($record);
176             }
177              
178 0           return $object;
179             }
180              
181             =head2 build_config_from_xml
182              
183             Extracts metadata for a given record type id from a XML config file, if a file was given.
184             Otherwise id and accessor are set to the given id and all other attributes are undef.
185              
186             XML::Simple must be installed in order to use this method.
187              
188             Format of the XML config file:
189              
190             <RecordTypes>
191             <RecordType id="theId" length="theLength" type="theType" accessor="theAccessor">
192             <label lang="en">TheEnglishLabel</label>
193             <label lang="de">TheGermanLabel</label>
194             <!-- more labels -->
195             </RecordType>
196             <!-- more record types -->
197             </RecordTypes>
198              
199             =cut
200              
201             sub build_config_from_xml {
202 0     0 1   my $file = shift;
203              
204 0 0         return [] unless (length $file);
205              
206 1     1   1129 use XML::Simple;
  1         9800  
  1         16  
207             return XML::Simple->new(
208             KeyAttr => { label => 'lang' },
209             GroupTags => { labels => 'label' },
210             ContentKey => '-content',
211 0           )->XMLin($file)->{RecordType};
212             }
213              
214             sub _next {
215 0     0     my $self = shift;
216 0           my $line;
217            
218 0           do {
219 0 0         $line = $self->fh->getline() or return undef;
220             } while ($line =~ /^\s*$/);
221              
222 0           my $record = xDT::Record->new($line);
223 0           $record->set_record_type(xDT::RecordType::build_from_arrayref(
224             substr($line, 3, 4),
225             $self->record_type_config,
226             ));
227              
228 0           return $record;
229             }
230              
231             =head1 AUTHOR
232              
233             Christoph Beger, C<< <christoph.beger at medizin.uni-leipzig.de> >>
234              
235             =cut
236              
237             __PACKAGE__->meta->make_immutable;
238              
239             1; # End of xDT::Parser