File Coverage

blib/lib/Data/Properties.pm
Criterion Covered Total %
statement 60 61 98.3
branch 29 36 80.5
condition 5 8 62.5
subroutine 8 8 100.0
pod 6 6 100.0
total 108 119 90.7


line stmt bran cond sub pod time code
1             # -*- Mode: Perl; indent-tabs-mode: nil -*-
2              
3             =pod
4              
5             =head1 NAME
6              
7             Data::Properties - persistent properties
8              
9             =head1 SYNOPSIS
10              
11             my $props = Data::Properties->new();
12              
13             open FH, "./my.properties" or
14             die "can't open my.properties: $!\n";
15             $props->load(\*FH);
16             close FH;
17              
18             for my $name ($props->property_names()) {
19             my $val = $props->get_property($name);
20             }
21              
22             $props->set_property("foo", "bar");
23              
24             open FH, "> ./new.properties" or
25             die "can't open new.properties: $!\n";
26             $props->store(\*FH);
27             close FH;
28              
29             =head1 DESCRIPTION
30              
31             This class is a Perl version of Java's B and
32             aims to be format-compatible with that class.
33              
34             The B class represents a persistent set of properties. The
35             B can be saved to a filehandle or loaded from a
36             filehandle. Each key and its corresponding value in the property list
37             is a string.
38              
39             A property list can contain another property list as its "defaults";
40             this second property list is searched if the property key is not found
41             in the original property ist.
42              
43             B does no type checking on the keys or values stored with
44             C. Keys and values are stored as strings via
45             C, so you almost always want to use simple keys and values,
46             not arrays, or hashes, or references. Keys and values are loaded and
47             stored "as-is"; no character or other conversions are performed on
48             them.
49              
50             =cut
51              
52             package Data::Properties;
53              
54             $VERSION = '1.07';
55              
56 1     1   6057 use strict;
  1         3  
  1         30  
57 1     1   560 use POSIX ();
  1         9471  
  1         789  
58              
59             =pod
60              
61             =head1 CONSTRUCTOR
62              
63             =over
64              
65             =item new([$defaults])
66              
67             Creates an empty property list, optionally with the specified
68             defaults.
69              
70             Dies if C<$defaults> is not a B object.
71              
72             =back
73              
74             =cut
75              
76             sub new {
77 3     3 1 70 my ($type, $defaults) = @_;
78 3   33     15 my $class = ref($type) || $type;
79              
80 3 100 100     17 if ($defaults && !UNIVERSAL::isa($defaults, __PACKAGE__)) {
81 1         7 die sprintf("Specified defaults object does not inherit from %s\n",
82             __PACKAGE__);
83             }
84              
85 2         11 my $self = {
86             _props => {},
87             _defaults => $defaults,
88             _lastkey => undef,
89             };
90              
91 2         10 return bless $self, $class;
92             }
93              
94             =pod
95              
96             =head1 METHODS
97              
98             =over
99              
100             =item get_property($key, [$default_value])
101              
102             Searches for the property with the specified key in this property
103             list. If the key is not found in this property list, the default
104             property list and its defaults are recursively checked. If the
105             property is not found, C<$default_value> is returned if specified, or
106             C otherwise.
107              
108             =cut
109              
110             sub get_property {
111 9     9 1 23 my ($self, $key, $default_value) = @_;
112 9   50     36 $default_value ||= "";
113              
114 9 50       17 return $default_value unless $key;
115 9 100       37 return $self->{_props}->{$key} if $self->{_props}->{$key};
116              
117 4 100       15 return $default_value unless $self->{_defaults};
118 2         7 return $self->{_defaults}->get_property($key);
119             }
120              
121             =pod
122              
123             =item load($handle)
124              
125             Reads a property list from the specified input handle.
126              
127             Every property occupies one line read from the input handle. Lines
128             from the input handle are processed until EOF is reached.
129              
130             A line that contains only whitespace or whose first non-whitespace
131             character is an ASCII C<#> or C is ignored (thus, these characters
132             indicate comment lines).
133              
134             Every line other than a blank line or a comment line describes one
135             property to be added to the property list (except that if a line ends
136             with C<\>, then the following line, if it exists, is treated as a
137             continuation line, as described below). The key consists of all the
138             characters in the line starting with the first non-whitespace
139             character and up to, but not including, the first ASCII C<=>, C<:>, or
140             whitespace character. Any whitespace after the key is skipped; if the
141             first non-whitespace character after the key is C<=> or C<:>, then it
142             is ignored and any whitespace characters after it are also
143             skipped. All remaining characters on th eline become part of the
144             associated value. If the last character on the line is C<\>, then the
145             next line is treated as a continuation of the current line; the C<\>
146             and line terminator are simply discarded, and any leading whitespace
147             characters on the continuation line are also discarded and not part of
148             the element string.
149              
150             As an example, each of the following lines specifies the key C<"Truth">
151             and the associated element value C<"Beauty">:
152              
153             Truth = Beauty
154             Truth:Beauty
155             Truth :Beauty
156              
157             As another example, the following three lines specify a single
158             property:
159              
160             fruits apple, banana, pear, \
161             cantaloupe, watermelon, \
162             kiwi, mango
163              
164             The key is C<"fruits"> and the associated element is C<"apple, banana,
165             pear, cantaloupe, watermelon, kiwi, mango">.
166              
167             Note that a space appears before each C<\> so that a space will appear
168             after each comma in the final value; the C<\>, line terminator, and
169             leading whitespace on the continuation line are merely discarded and
170             are C replaced by one or more characters.
171              
172             As a third example, the line:
173              
174             cheeses:
175              
176             specifies that the key is C<"cheeses"> and the associated element is
177             the empty string.
178              
179             Dies if an error occurs when reading from the input handle.
180              
181             =cut
182              
183             sub load {
184 1     1 1 4 my ($self, $in) = @_;
185 1 50       4 return undef unless $in;
186              
187 1         2 my ($key, $val, $is_continuation, $is_continued);
188 1         2 local $_;
189 1         30 while (defined($_ = <$in>)) {
190 14 100       34 next if /^[#!]/; # leading # or ! signifies comment
191 13 100       46 next if /^\s+$/; # all-whitespace
192              
193 7         9 chomp;
194              
195 7 100       23 if ($is_continuation) {
196             # don't attempt to parse a key on a continuation line
197 2         5 s/^\s*//;
198 2         4 undef $key;
199             } else {
200             # regular line - parse out the key
201 5         20 s/^\s*([^=:\s]+)\s*[=:\s]\s*//;
202 5         12 $key = $1;
203             }
204              
205 7 100       20 $is_continued = s/\\$// ? 1 : undef;
206 7         11 $val = $_;
207              
208 7 100       13 if ($is_continuation) {
    50          
209             # append the continuation value to the value of the
210             # last key
211 2         6 $self->{_props}->{$self->{_lastkey}} .= $val;
212             } elsif ($key) {
213 5         13 $self->{_props}->{$key} = $val;
214             } else {
215 0         0 warn "Malformed property line: $_\n";
216             }
217              
218 7 100       10 if ($is_continued) {
219 2         3 $is_continuation = 1;
220             # allow for continuation lines being continued
221 2 100       9 $self->{_lastkey} = $key if defined $key;
222             } else {
223 5         7 undef $is_continuation;
224 5         22 undef $self->{_lastkey};
225             }
226             }
227              
228 1         8 return 1;
229             }
230              
231             =pod
232              
233             =item property_names
234              
235             Returns an array (or an arrayref in scalar context) containing all of
236             the keys in this property list, including the keys in the default
237             property list.
238              
239             =cut
240              
241             sub property_names {
242 2     2 1 5 my ($self) = @_;
243              
244 2         4 my @names = keys %{$self->{_props}};
  2         17  
245 2 100       12 push @names, $self->{_defaults}->property_names() if $self->{_defaults};
246              
247 2 50       14 return wantarray ? @names : \@names;
248             }
249              
250             =pod
251              
252             =item set_property($key, $value)
253              
254             Sets the property with the specified key.
255              
256             =cut
257              
258             sub set_property {
259 1     1 1 3 my ($self, $key, $value) = @_;
260 1 50       4 return undef unless $key;
261              
262 1         3 $self->{_props}->{$key} = $value;
263              
264 1         4 return 1;
265             }
266              
267             =pod
268              
269             =item store($handle, $header)
270              
271             Writes this property list to the specified output handle. Default
272             properties are I written by this method.
273              
274             If a header is specified, then the ASCII characters C<# >, the header
275             string, and a line separator are first written to the output
276             handle. Thus the header can serve as an identifying comment.
277              
278             Next, a comment line is always written, consisting of the ASCII
279             characters C<# >, the current date and time (as produced by
280             C), and a line separator.
281              
282             Then every entry in the property list is written out, one per
283             line. For each entry the key string is written, then an ASCII C<=>,
284             then the associated value.
285              
286             The output handle remains open after this method returns.
287              
288             Dies if an error occurs when writing to the input handle.
289              
290             =cut
291              
292             sub store {
293 1     1 1 4 my ($self, $out, $header) = @_;
294 1 50       14 return undef unless $out;
295              
296 1         6 local $| = 1;
297              
298 1 50       27 print $out "# $header\n", if $header;
299 1         47 print $out "# ", POSIX::ctime(time), "\n";
300              
301 1         3 for my $k (sort keys %{$self->{_props}}) {
  1         9  
302 5         16 print $out sprintf("%s=%s\n", $k, $self->{_props}->{$k});
303             }
304              
305 1         8 return 1;
306             }
307              
308             1;
309             __END__