File Coverage

blib/lib/DeltaX/Language.pm
Criterion Covered Total %
statement 65 100 65.0
branch 21 44 47.7
condition 1 5 20.0
subroutine 7 9 77.7
pod 4 4 100.0
total 98 162 60.4


line stmt bran cond sub pod time code
1             #-----------------------------------------------------------------
2             package DeltaX::Language;
3             #-----------------------------------------------------------------
4             # $Id: Language.pm,v 1.1 2003/03/17 13:01:36 spicak Exp $
5             #
6             # (c) DELTA E.S., 2002 - 2003
7             # This package is free software; you can use it under "Artistic License" from
8             # Perl.
9             #-----------------------------------------------------------------
10              
11             $DeltaX::Language::VERSION = '1.0';
12              
13 1     1   324 use strict;
  1         1  
  1         22  
14 1     1   3 use Carp;
  1         1  
  1         922  
15              
16             #-----------------------------------------------------------------
17             sub new {
18             #-----------------------------------------------------------------
19             # CONSTRUCTOR
20             #
21 3     3 1 46 my $pkg = shift;
22 3         4 my $self = {};
23 3         4 bless ($self, $pkg);
24              
25 3         4 my $filename = shift;
26 3 50       6 croak ("You must supply filename!") unless defined $filename;
27 3         6 $self->{filename} = $filename;
28 3 50       7 if (@_ % 2) {
29 0   0     0 $self->{separator} = shift || "\t";
30             } else {
31 3         4 $self->{separator} = "\t";
32             }
33 3         3 $self->{error} = '';
34 3         3 $self->{conflicts}= ();
35              
36 3 50       7 croak ("$pkg created with odd number of parameters - should be of the form option => value")
37             if (@_ % 2);
38 3         8 for (my $x = 0; $x <= $#_; $x += 2) {
39 0         0 $self->{special}{$_[$x]} = $_[$x+1];
40             }
41              
42 3         6 return $self;
43             }
44             # END OF new()
45              
46             #-----------------------------------------------------------------
47             sub read {
48             #-----------------------------------------------------------------
49             #
50 3     3 1 17 my $self = shift;
51              
52 3         4 local(*INF);
53 3 50       78 if (! open INF, $self->{filename}) {
54 0         0 $self->{error} = "cannot read file '".$self->{filename}."': $!";
55 0         0 return undef;
56             }
57              
58 3         4 my %ret;
59              
60 3         4 my $sep = $self->{separator};
61 3         38 while () {
62 10         10 chomp;
63 10 100       22 if (/^[ ]*#/) {
64 3         9 s/^[ ]*#[ ]*//g;
65 3 100       12 if (/^!(.*)$/) {
66 1         3 my $tmp = $self->_special($1);
67 1 50       4 if (! defined $tmp) { return undef; }
  0         0  
68 1         2 foreach my $key (keys %{$tmp}) {
  1         3  
69 1 50       2 if (exists $ret{$key}) {
70 0         0 push @{$self->{conflicts}}, $key;
  0         0  
71             }
72 1         3 $ret{$key} = $tmp->{$key};
73             }
74             # separator may changed
75 1         5 $sep = $self->{separator};
76             }
77             } else {
78 7         29 my ($key, $val) = split(/$sep/, $_, 2);
79 7 100       15 next if !$key;
80 4 50       5 $val = $val ? $val : '';
81 4 50       9 if (exists $ret{$key}) {
82 0         0 push @{$self->{conflicts}}, $key;
  0         0  
83             }
84 4 50 33     14 if ($val and $val =~ /^%/) {
85 0 0       0 if (exists $ret{substr($val, 1)}) {
86 0         0 $val = $ret{substr($val, 1)};
87             }
88             }
89 4         14 $ret{$key} = $val;
90             }
91            
92             }
93 3         12 close INF;
94              
95 3         9 return \%ret;
96             }
97             # END OF read()
98              
99              
100             #-----------------------------------------------------------------
101             sub get_error {
102             #-----------------------------------------------------------------
103             #
104 0     0 1 0 my $self = shift;
105              
106 0         0 return $self->{error};
107             }
108             # END OF get_error()
109              
110             #-----------------------------------------------------------------
111             sub get_conflicts() {
112             #-----------------------------------------------------------------
113              
114 0     0 1 0 my $self = shift;
115              
116 0         0 return @{$self->{conflicts}};
  0         0  
117             }
118             # END OF get_conflicts()
119              
120              
121             #-----------------------------------------------------------------
122             sub _special {
123             #-----------------------------------------------------------------
124             #
125 1     1   2 my $self = shift;
126 1         3 my $token = shift;
127              
128 1         3 $token =~ s/^\s*//g;
129            
130 1 50       4 if ($token =~ /^include/) {
131 1         2 $token =~ /^include\s+(\S+)\s*$/;
132 1         4 return $self->_include($1);
133             }
134 0 0       0 if ($token =~ /^separator/) {
135 0         0 $token =~ /^separator\s+(\S+)\s*$/;
136 0         0 $self->{separator} = "$token";
137 0         0 my %tmp;
138 0         0 return \%tmp;
139             }
140              
141 0         0 $token =~ /^(\S+)\s*(.*)$/s;
142 0         0 my @args;
143 0 0       0 if ($2) { @args = split(/,/, $2); }
  0         0  
144             # other special command
145 0 0       0 if (! exists $self->{special}{$1}) {
146 0         0 $self->{error} = "unknown directive '$1'";
147 0         0 return undef;
148             }
149 0         0 return $self->{special}{$1}->(@args);
150              
151             }
152             # END OF _special
153              
154             #-----------------------------------------------------------------
155             sub _include {
156             #-----------------------------------------------------------------
157             #
158 1     1   1 my $self = shift;
159 1         2 my $arg = shift;
160              
161             # relative path!
162 1 50       3 if ($arg !~ /^\//) {
163 1 50       4 if ($self->{filename} =~ /^(.*)\/[^\/]*$/) {
164 1 50       3 if ($self->{special}{'include'}) {
165 0         0 $arg = $self->{special}{'include'}->($arg);
166             } else {
167 1         10 $arg = "$1/$arg";
168             }
169             }
170             }
171 1 50       3 if (!$arg) {
172 0         0 $self->{error} = "include: no file found";
173 0         0 return undef;
174             }
175              
176 1         5 my @spec;
177 1         2 foreach my $s (sort keys %{$self->{special}}) {
  1         5  
178 0         0 push @spec, $s, $self->{special}{$s};
179             }
180 1         4 my $inc = new DeltaX::Language($arg, @spec);
181 1         4 my $ret = $inc->read();
182 1 50       4 if (! defined $ret) {
183 0         0 $self->{error} = "include: unable to read '$arg': ". $inc->get_error();
184 0         0 return undef;
185             }
186 1         2 return $ret;
187             }
188             # END OF _include()
189              
190             #-----------------------------------------------------------------
191             sub DESTROY {
192             #-----------------------------------------------------------------
193             #
194 3     3   11 my $self = shift;
195              
196             }
197             # END OF DESTROY()
198              
199             1;
200              
201             =head1 NAME
202              
203             DeltaX::Language - Perl module for reading language files
204              
205             _____
206             / \ _____ ______ ______ ___________
207             / \ / \\__ \ / ___// ___// __ \_ __ \
208             / Y \/ __ \_\___ \ \___ \\ ___/| | \/
209             \____|__ (____ /____ >____ >\___ >__|
210             \/ \/ \/ \/ \/ project
211              
212              
213             =head1 SYNOPSIS
214              
215             use DeltaX::Language;
216              
217             my $lang_file = new DeltaX::Language('my_lang.EN');
218             my $texts = $lang_file->read();
219             my @conflicts = $lang_file->get_conflicts();
220              
221             print $texts->{'text_id'};
222              
223             =head1 FUNCTIONS
224              
225             =head2 new()
226              
227             Constructor. The first argument is a filename (required), second (optional)
228             field separator (default is a tabelator ('\t')), other arguments are in key =>
229             value form, they are directive definitions in "directive_name => sub reference"
230             form (see L<"DIRECTIVES">).
231              
232             =head2 read()
233              
234             This function reads given file and returns undef (in case of error) or reference
235             to a hash in which keys are text id's and values a texts themselfs.
236              
237             =head2 get_error()
238              
239             This function returns error in textual form (only valid after read() call).
240              
241             =head2 get_conflicts()
242              
243             This function returns array with text id's which occured more than once (in
244             result of read() function will be only the last one). Only valid after read()
245             call.
246              
247             =head1 TEXT FILE STRUCTURE
248              
249             Text files have a simple structure: one line = one record, in keytext
250             form. Everything form # sign to end of line is a comment, except #!
251             (see L<"DIRECTIVES">). Everything other (empty lines, ...) is ignored.
252              
253             Example:
254            
255             # this is comment
256             #
257             id1_This is text
258             id2_This is another text
259             #!include other_file
260              
261             [character _ means separator, often tabelator ('\t')]
262              
263             =head1 DIRECTIVES
264              
265             Directives are special form of comments: C<#!directive [parameters]>.
266             DeltaX::Config knows two of them:
267              
268             =over
269              
270             =item include
271              
272             It includes given file. Filename of included file is the first and only
273             argument. If it is not absolute path, path is got from actually readed filename.
274              
275             =item separator
276              
277             It sets given character as a new separator, it can be in escaped form (for
278             example #!separator \t). You cannot set new line or space as a separator using
279             this directive.
280              
281             =item every other
282              
283             By setting parameter to new() you can define other directives and use it in your
284             files. Every definition must be sub reference. This sub will be called with all
285             arguments for this directive.
286              
287             Program:
288              
289             sub myspec_func {
290             my $arg = shift;
291              
292             # return reference to a hash or undef in case of error
293             }
294              
295             my $conf = new DeltaX::Language('my_lang.EN',myspec=>\&myspec_func);
296              
297             Configuration file:
298              
299             #!myspec something
300              
301             =back
302              
303             =cut