File Coverage

blib/lib/WWW/Webrobot/Properties.pm
Criterion Covered Total %
statement 129 135 95.5
branch 40 60 66.6
condition 5 9 55.5
subroutine 17 18 94.4
pod 2 12 16.6
total 193 234 82.4


line stmt bran cond sub pod time code
1             package WWW::Webrobot::Properties;
2 29     29   25851 use strict;
  29         49  
  29         908  
3 29     29   141 use warnings;
  29         49  
  29         790  
4              
5             # Author: Stefan Trcek
6             # Copyright(c) 2004 ABAS Software AG
7              
8              
9 29     29   154 use Carp;
  29         54  
  29         58539  
10              
11              
12             sub new {
13 9     9 1 21921 my $class = shift;
14 9   33     84 my $self = bless({}, ref($class) || $class);
15 9         31 my %parm = (@_); # accept parameter list as hash
16              
17             # check for duplicates in 'listmode'
18 9         17 my %listmode = ();
19 9         14 foreach (@{$parm{listmode}}) {
  9         30  
20 12 50       51 croak "'$_' defined twice in 'listmode'" if $listmode{$_}++;
21             }
22              
23             # check for duplicates in 'key_value' and 'multi_value'
24 9         14 my %duplicate;
25 9         16 foreach ((@{$parm{key_value}}, @{$parm{multi_value}})) {
  9         18  
  9         24  
26 8 50       34 croak "'$_' defined twice in 'key_value' or 'multi_value'" if $duplicate{$_}++;
27             # add key_value/muli_value items to listmode if not already specified
28 8 100       249 push @{$parm{listmode}}, $_ if ! $listmode{$_}++;
  2         8  
29             }
30 9 50       44 $self->{_listmode} = $parm{listmode} if defined $parm{listmode};
31 9 50       33 $self->{_listmode_hash} = \%listmode if defined $parm{listmode};
32 9 50       33 $self->{_key_value} = $parm{key_value} if defined $parm{key_value};
33 9 50       30 $self->{_multi_value} = $parm{multi_value} if defined $parm{multi_value};
34 9 100       26 $self->{_structurize} = $parm{structurize} if defined $parm{structurize};
35 9         42 return $self;
36             }
37              
38             sub property {
39 212     212 0 301 my ($self, $name, $value) = @_;
40 212 100       872 $self->{prop}->{$name} = $value if defined $value; # setter
41 212 50       874 return $self->{prop}->{$name} if defined $name; # setter/getter
42 0         0 return $self->{prop}; # return hash of properties
43             }
44              
45             sub clear_properties {
46 9     9 0 11 my ($self) = @_;
47 9         20 $self->{prop} = {};
48 9 50       25 if ($self->{_listmode}) {
49 9         11 foreach (@{$self->{_listmode}}) {
  9         22  
50 14         35 $self->property($_, []);
51             }
52             }
53             }
54              
55             sub make_key_value {
56 9     9 0 15 my $self = shift;
57 9         12 foreach my $prop (@{$self->{_key_value}}) {
  9         24  
58 5         11 my @list = ();
59 5         5 foreach my $elem (@{$self->{prop}->{$prop}}) {
  5         17  
60 19         76 my ($key, $value) = split /\s*=\s*/, $elem, 2;
61 19 50 33     137 push @list, [$key, $value] if defined $key && $key =~ m/./;
62             }
63 5         26 $self->{prop}->{$prop} = \@list;
64             }
65             }
66              
67             sub make_multi_value {
68 9     9 0 13 my $self = shift;
69 9         11 foreach my $prop (@{$self->{_multi_value}}) {
  9         23  
70 3         16 foreach my $elem (@{$self->{prop}->{$prop}}) {
  3         10  
71 9         38 my ($split_char, $rest) = $elem =~ /^(.)(.*)$/;
72 9         15 $split_char = quotemeta $split_char;
73 9         133 my @list = split /$split_char/, $rest;
74 9         27 $elem = \@list;
75             }
76             }
77             }
78              
79             sub structurize {
80 9     9 1 11 my $self = shift;
81 9         9 foreach my $prop (@{$self->{_structurize}}) {
  9         26  
82 2         5 foreach (keys %{$self->{prop}}) {
  2         10  
83 18         89 my ($end) = /^$prop\.(.*)$/;
84 18 100       49 if ($end) {
85 10         29 $self->{prop}->{$prop}->{$end} = $self->{prop}->{$_};
86 10         26 delete $self->{prop}->{$_};
87             }
88             }
89             }
90             }
91              
92             # private
93             sub _load_basic {
94 9     9   15 my ($self, $input, $cmd_properties) = @_;
95 9 50       25 croak "No handle specified" if !defined $input;
96 9         25 $self->clear_properties();
97 9         19 my $p = "";
98 9         19 while (defined $input->()) {
99 147         288 chomp;
100 147 100       349 if (m/.*\\$/) {
101 10         18 chop;
102 10         17 $p .= $_;
103 10         17 next;
104             }
105 137 100       233 if ($p) {
106 5         16 s/^\s*//;
107 5         12 $_ = $p . $_;
108 5         8 $p = "";
109             }
110              
111 137 100 100     1962 next if /^\s*[#!]/ || /^\s*$/; # skip comment, lines containing white space only
112 99         916 s/(\\ |[^\s\\])\s+$/$1/; # skip trailing white space except '\ ' and '\'
113 99         555 my ($key, $tmp0, $tmp1, $value) = /^\s*(([^=: ])+)\s*([=:])?\s*(.*)$/;
114 99 50       225 $key = "" if !defined $key;
115 99 50       186 $value = "" if !defined $value;
116              
117 99 50       174 if ($key ne "") {
118 99         2161 (my $new_key = $key) =~ s/^(.*)\.\d+$/$1/;
119 99 50       190 $new_key = "" if !defined $new_key;
120 99 100       252 $key = $new_key if $self->{_listmode_hash}->{$new_key};
121              
122 99 100       187 if (ref $self->property($key) eq 'ARRAY') {
123 48         57 push @{$self->property($key)}, $value;
  48         81  
124             }
125             else {
126 51         93 $self->property($key, $value);
127             }
128             }
129             }
130              
131             #use Data::Dumper; print STDERR Dumper $self->{prop};
132 9         25 $self->property(@$_) foreach (@$cmd_properties);
133 9         38 $self->make_key_value();
134 9         24 $self->make_multi_value();
135 9         21 $self->structurize();
136 9         27 unescape($self->{prop});
137             #use Data::Dumper; print STDERR Dumper $self->{prop};
138 9         68 return $self->{prop};
139             }
140              
141             sub unescape0 {
142 130     130 0 154 my ($prop) = @_;
143 130         802 $prop =~ s/\\n/\n/g;
144 130         136 $prop =~ s/\\r/\t/g;
145 130         127 $prop =~ s/\\t/\t/g;
146 130         141 $prop =~ s/\\(["' ])/$1/g;
147             # \uxxxx not implemented
148 130         392 return $prop;
149             }
150              
151             sub unescape {
152 53     53 0 58 my ($prop) = @_;
153             #return if ! defined $prop;
154 53 100       124 if (ref $prop eq 'ARRAY') {
    50          
155 42         55 foreach (@$prop) {
156 113 100       164 if (ref) {
157 28         44 unescape($_);
158             }
159             else {
160 85         118 $_ = unescape0($_);
161             }
162             }
163             }
164             elsif (ref $prop eq 'HASH') {
165 11         45 foreach (keys %$prop) {
166 61         87 my $value = $prop->{$_};
167 61 100       142 if (ref $value) {
168 16         29 unescape($value);
169             }
170             else {
171 45         66 $prop->{$_} = unescape0($value);
172             }
173             }
174             }
175             else {
176 0         0 die "ARRAY, HASH or scalar expected";
177             }
178             }
179              
180             sub load_string {
181 8     8 0 28 my ($self, $string, $cmd_properties) = @_;
182             return $self->_load_basic(sub {
183 129     129   660 (my $str, $string) = $string =~ m/^([^\n]*)\n(.*)$/s;
184 129         365 return $_ = $str;
185 8         48 }, $cmd_properties);
186             }
187              
188             sub load_handle {
189 1     1 0 7 my ($self, $handle, $cmd_properties) = @_;
190 1     27   11 return $self->_load_basic(sub {$_ = <$handle>; return $_;}, $cmd_properties);
  27         81  
  27         69  
191             }
192              
193             sub load_file {
194 1     1 0 11 my ($self, $filename, $cmd_properties) = @_;
195 1         5 local *HANDLE;
196 1 50       73 open HANDLE, "<$filename" or croak "Can't open $filename: $!";
197 1         7 my $cfg = $self->load_handle(*HANDLE, $cmd_properties);
198 1         21 close HANDLE;
199 1         8 return $cfg;
200             }
201              
202             sub load {
203 0     0 0   my ($self, $source, $cmd_properties) = @_;
204 0 0         if (ref $source eq 'SCALAR') {
    0          
205 0           return $self->load_file($$source, $cmd_properties);
206             }
207             elsif (! ref $source) {
208 0           return $self->load_string($source, $cmd_properties);
209             }
210             }
211              
212             1;
213              
214             =head1 NAME
215              
216             WWW::Webrobot::Properties - Implements a config format like java.util.Properties
217              
218             =head1 SYNOPSIS
219              
220             my $config = WWW::Webrobot::Properties->new(
221             listmode => [qw(names auth_basic output http_header proxy no_proxy)],
222             key_value => [qw(names http_header proxy)],
223             multi_value => [qw(auth_basic)],
224             structurize => [qw(load mail)],
225             );
226             my $cfg = $config->load_file($cfg_name, $cmd_param);
227              
228              
229             =head1 DESCRIPTION
230              
231             This class implements a config format like java.util.Properties,
232             see
233             L
234             for more docs.
235              
236             B
237             Some features are not implemented but there are some extensions for lists.
238              
239             =head2 NOT IMPLEMENTED
240              
241             \uxxxx Unicode characters
242              
243             =head2 EXTENDED FORMAT
244              
245             Listmode properties may be written
246              
247             listprop=value0
248             listprop=value1
249             listprop=value2
250              
251             or
252              
253             listprop.0=value0
254             listprop.1=value1
255             listprop.2=value2
256              
257             These properties are made available as perl-arrays.
258              
259             =head1 METHODS
260              
261             =over
262              
263             =item $wr = WWW::Webrobot::Properties -> new(%options);
264              
265             Construct an object.
266             Options marked (F) affect the semantics of the properties format.
267             All options affect the internal representation in Perl.
268             The syntax is
269              
270             option => [...]
271              
272             =over
273              
274             =item listmode (F)
275              
276             Multiple definitions enforce an array of options.
277             Multiple definition options may (but needn't) be
278             written with additional digits:
279              
280             names.1=xxx
281             names.27=yyy
282              
283             or
284              
285             names=xxx
286             names=yyy
287              
288             Thats for compatibility to java.util.Properties.
289              
290             =item key_value (F)
291              
292             Option value as 'key=value' deparsed.
293              
294             names = key=value
295              
296             =item multi_value (F)
297              
298             Option value as '/v0/v1/v2/v3...' deparsed as array
299             / is any literal character
300             names = /v0/v1/v2/v3/
301              
302             =item structurize (-)
303              
304             Common prefix options deparse as hash, e.g.
305              
306             load.num=xx
307             load.base=yy
308              
309             yields in the internal config format
310              
311             load => {num => "xx", base => "yy"}
312              
313             =back
314              
315             For a complete guide of the semantics of the options
316             see the tests F.
317              
318             =back
319