FreeHAL
view release on metacpan or search on metacpan
AI/FreeHAL/Config.pm view on Meta::CPAN
package AI::FreeHAL::Config;
#use version;
$VERSION = '0.0.4';
my %global_def_sep;
sub import {
my ($package, $opt_ref) = @_;
my $caller = caller();
$global_def_sep{$caller} = $opt_ref->{def_sep};
for my $sub_name (qw( read_config write_config )) {
$opt_ref->{$sub_name} ||= $sub_name;
}
*{$caller.'::'.$opt_ref->{read_config}} = \&AI::FreeHAL::Config::Hash::read_config;
*{$caller.'::'.$opt_ref->{write_config}} = \&AI::FreeHAL::Config::Hash::write_config;
}
package AI::FreeHAL::Config::Gap;
use AI::FreeHAL::Class;
### [caller 0]
{
sub serialize { return "\n" }
sub update {}
sub extend {}
sub copy_to {}
}
package AI::FreeHAL::Config::Comment;
use AI::FreeHAL::Class;
{
my %text_of : ATTR( :init_arg<text> );
sub serialize {
my ($self) = @_;
return $text_of{ident $self};
}
sub append_comment {
my ($self, $new_text) = @_;
$text_of{ident $self} .= $new_text;
}
sub update {}
sub extend {}
sub copy_to {}
}
package AI::FreeHAL::Config::Keyval;
use AI::FreeHAL::Class;
{
my %key_of : ATTR( :init_arg<key> :get<key> );
my %vals_of : ATTR;
my %deleted_of : ATTR;
sub BUILD {
my ($self, $ident, $arg_ref) = @_;
$vals_of{$ident} = [ { %{$arg_ref} } ];
}
my %SEPARATOR = ( ':' => ': ', '=' => ' = ' );
use Carp;
sub serialize {
my ($self, $def_sep, $block_name) = @_;
my $ident = ident $self;
return "" if $deleted_of{$ident};
my ($key, $vals) = ($key_of{$ident}, $vals_of{$ident});
my $keyspace = q{ } x length($key);
my $serialization = q{};
for $n (0..$#{$vals}) {
my ($val,$sep,$comm) = @{$vals->[$n]}{qw(val sep comm)};
my $val_type = ref $val;
croak qq{Can't save \L$val_type\E ref as value for key {'$block_name'}{'$key'} (only scalars or array refs)}
if $val_type && $val_type ne 'ARRAY';
$sep = $SEPARATOR{$sep || $def_sep};
my @vals = $val_type eq 'ARRAY' ? @{$val} : $val;
s/ (?!\Z) \n /\n$keyspace$sep/gxms for @vals;
$serialization .= $comm || q{};
$serialization .= join q{}, map {"$key$sep$_\n"} @vals;
}
return $serialization;
}
sub update {
my ($self, $hash_ref, $updated_ref) = @_;
my $ident = ident $self;
my $key = $key_of{$ident};
if (!exists $hash_ref->{$key}) {
$deleted_of{$ident} = 1;
}
else {
my $val = $hash_ref->{$key};
@newvals = ref $val eq 'ARRAY' ? @{$val} : $val;
for my $n (0..$#newvals) {
$vals_of{$ident}[$n]{val} = $newvals[$n];
}
splice @{$vals_of{$ident}}, scalar @newvals;
}
$updated_ref->{$key} = 1;
return 1;
}
sub copy_to {
my ($self, $hash_ref) = @_;
my $ident = ident $self;
my @vals = map $_->{val}, @{$vals_of{$ident}};
$hash_ref->{$key_of{$ident}} = @vals > 1 ? \@vals : $vals[0];
}
sub multivalue {
my ($self, $sep, $val, $comm) = @_;
push @{$vals_of{ident $self}}, {val=>$val, sep=>$sep, comm=>$comm};
}
}
package AI::FreeHAL::Config::Block;
use AI::FreeHAL::Class;
{
my %name_of : ATTR( :init_arg<name> :get<name> default => '' );
my %sep_count_of : ATTR;
my %precomm_of : ATTR( :init_arg<precomm> default => '' );
my %parcomm_of : ATTR( :init_arg<parcomm> default => '' );
my %components_of : ATTR;
my %deleted_of : ATTR;
my %seen : ATTR;
my %is_first : ATTR( :init_arg<first> default => '' );
sub BUILD {
my ($self, $ident) = @_;
@{$sep_count_of{$ident}}{':','='} = (0,0);
$components_of{$ident} = [];
$seen{$ident} = {};
}
sub copy_to {
my ($self, $hash_ref) = @_;
my $ident = ident $self;
my $keyvals = $hash_ref->{$name_of{$ident}} ||= {};
for my $comp ( @{$components_of{$ident}} ) {
$comp->copy_to($keyvals);
}
$hash_ref->{$name_of{$ident}} = $keyvals;
}
sub serialize {
my ($self, $first, $caller, $post_gap) = @_;
my $ident = ident $self;
return q{} if $deleted_of{$ident};
my $is_anon = $first && length($name_of{$ident}) == 0;
my $serialization = q{};
if (!$is_anon) {
$serialization = ($precomm_of{$ident} || q{})
. "[$name_of{$ident}]"
. (defined $parcomm_of{$ident}?$parcomm_of{$ident}:q{})
. "\n";
}
my $gds = $global_def_sep{$caller};
my $def_sep
= defined $gds ? $gds
: $sep_count_of{$ident}{':'} >= $sep_count_of{$ident}{'='} ? ':'
: '='
;
$self->ensure_gap() if !$is_anon;
for my $comp ( @{$components_of{$ident}} ) {
$serialization .= $comp->serialize($def_sep, $name_of{$ident});
}
return $serialization;
}
sub update {
my ($self, $hash_ref, $updated_ref) = @_;
my $ident = ident $self;
if (!defined $hash_ref) {
$deleted_of{$ident} = 1;
return;
}
for my $comp ( @{$components_of{$ident}} ) {
$comp->update($hash_ref, $updated_ref) or next;
}
}
sub extend {
my ($self, $hash_ref, $updated_ref, $post_gap) = @_;
# Only the first occurrence of a block has new keys added...
return unless $is_first{ident $self};
my $first = 1;
for my $key ( grep {!$updated_ref->{$_}} keys %{$hash_ref}) {
$self->ensure_gap() if !$first++ || $post_gap;
$self->add_keyval($key, undef, $hash_ref->{$key});
}
}
sub ensure_gap {
my ($self) = @_;
my $comp_ref = $components_of{ident $self};
return if @{$comp_ref} && $comp_ref->[-1]->isa('AI::FreeHAL::Config::Gap');
push @{$comp_ref}, AI::FreeHAL::Config::Gap->new();
}
sub add_gap {
my ($self) = @_;
push @{$components_of{ident $self}}, AI::FreeHAL::Config::Gap->new();
}
sub add_comment {
my ($self, $text) = @_;
my $comp_ref = $components_of{ident $self};
if ($comp_ref && @{$comp_ref} && $comp_ref->[-1]->isa('AI::FreeHAL::Config::Comment') ) {
$comp_ref->[-1]->append_comment($text);
}
else {
push @{$comp_ref}, AI::FreeHAL::Config::Comment->new({text=>$text});
}
}
sub add_keyval {
my ($self, $key, $sep, $val, $comm) = @_;
my $ident = ident $self;
$sep_count_of{$ident}{$sep}++ if $sep;
my $seen = $seen{$ident};
if ($seen->{$key}) {
$seen->{$key}->multivalue($sep, $val, $comm);
return;
( run in 2.472 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )