package Lire::Config::SpecParser;
use strict;
use Lire::Config::TypeSpec;
use Lire::Utils qw/ check_param /;
use Lire::Error qw/directory_not_readable/;
use base qw/Lire::DocBookParser Lire::Config::Parser/;
use Carp;
=pod
=head1 NAME
Lire::Config::SpecParser - Create configuration specification from XML files
=head1 SYNOPSIS
use Lire::Config::SpecParser;
my $parser = new Lire::Config::SpecParser;
my $spec = $parser->parsefile( 'spec.xml' );
$parser->merge_specifications_dir( '/etc/lire/config' );
my $spec = $parser->configspec();
=head1 DESCRIPTION
This is an Lire::XMLParser which can build Lire::Config::ConfigSpec
objects from an XML file.
=cut
sub namespaces {
my $self = $_[0];
return { %{$self->Lire::Config::Parser::namespaces()},
'http://www.logreport.org/LRCSML/' => 'lrcsml'
};
}
my %elements2class =
( 'lrcsml:boolean' => 'Lire::Config::BooleanSpec',
'lrcsml:command' => 'Lire::Config::CommandSpec',
'lrcsml:dlf-converter' => 'Lire::Config::DlfConverterSpec',
'lrcsml:dlf-schema' => 'Lire::Config::DlfSchemaSpec',
'lrcsml:dlf-streams' => 'Lire::Config::DlfStreamsSpec',
'lrcsml:directory' => 'Lire::Config::DirectorySpec',
'lrcsml:executable' => 'Lire::Config::ExecutableSpec',
'lrcsml:file' => 'Lire::Config::FileSpec',
'lrcsml:integer' => 'Lire::Config::IntegerSpec',
'lrcsml:list' => 'Lire::Config::ListSpec',
'lrcsml:object' => 'Lire::Config::ObjectSpec',
'lrcsml:option' => 'Lire::Config::OptionSpec',
'lrcsml:output-format' => 'Lire::Config::OutputFormatSpec',
'lrcsml:plugin' => 'Lire::Config::PluginSpec',
'lrcsml:record' => 'Lire::Config::RecordSpec',
'lrcsml:reference' => 'Lire::Config::ReferenceSpec',
'lrcsml:report-config' => 'Lire::Config::ReportSpec',
'lrcsml:select' => 'Lire::Config::SelectSpec',
'lrcsml:string' => 'Lire::Config::StringSpec',
);
my @scalar_mix = qw/ lrcsml:boolean lrcsml:command lrcsml:directory
lrcsml:dlf-converter lrcsml:dlf-schema lrcsml:dlf-streams
lrcsml:executable lrcsml:output-format
lrcsml:report-config lrcsml:reference
lrcsml:file lrcsml:integer lrcsml:string /;
my @compound_mix = qw/ lrcsml:list lrcsml:record lrcsml:object /;
my @specs_mix = ( qw/ lrcsml:select lrcsml:plugin/, @scalar_mix,
@compound_mix ),
my @infos_mix = qw/lrcsml:summary lrcsml:description/;
my %spec =
(
'lrcsml:config-spec' => [ @specs_mix ],
'lrcsml:summary' => { 'start' => 'collector_start',
'char' => 'collector_char',
'end' => 'summary_end',
'content' => [ 'PCDATA' ], },
'lrcsml:description' => { 'start' => 'dbk_init',
'char' => 'dbk_element_char',
'end' => 'description_end',
'content' => [ @Lire::DocBookParser::top_levels ],
},
'lrcsml:select' => { 'start' => 'spec_start',
'end' => 'spec_end',
'content' => [ @infos_mix, 'lrcsml:option',
'lrcml:param' ] },
'lrcsml:plugin' => { 'start' => 'spec_start',
'end' => 'spec_end',
'content' => [ @infos_mix, 'lrcsml:option',
'lrcml:param' ] },
'lrcsml:option' => { 'start' => 'spec_start',
'end' => 'spec_end',
'content' => [ @infos_mix ] },
);
foreach my $name ( @scalar_mix ) {
$spec{$name} = { 'start' => 'spec_start',
'end' => 'spec_end',
'content' => [ @infos_mix, 'lrcml:param' ] };
}
foreach my $name ( @compound_mix ) {
$spec{$name} = { 'start' => 'spec_start',
'end' => 'spec_end',
'content' => [ @infos_mix, @specs_mix, 'lrcml:param' ] };
}
sub elements_spec {
my $self = $_[0];
return {
%{$self->Lire::Config::Parser::elements_spec()},
%{$self->Lire::DocBookParser::elements_spec()},
%spec,
};
}
=pod
=head2 new()
Creates a new parser object.
=cut
sub new {
my $self = shift->SUPER::new( @_ );
$self->{'_spec'} = new Lire::Config::ConfigSpec();
return $self;
}
=pod
=head2 configspec()
Returns the Lire::Config::ConfigSpec object parsed.
=cut
sub configspec {
return $_[0]->{'_spec'};
}
=pod
=head2 merge_specifications_dir( $dir )
Parses all the files ending in '.xml' in the $directory. The types
defined in these files will be merged to the ConfigSpec object of this
parser.
=cut
sub merge_specifications_dir {
my ( $self, $dir ) = @_;
check_param( $dir, 'dir' );
opendir(my $dh, $dir)
or die directory_not_readable( $dir ), "\n";
foreach my $file (readdir($dh)) {
next unless $file =~ /\.xml$/;
$self->merge_specification("$dir/$file");
}
close($dh);
return;
}
=pod
=head2 merge_specification_dir( $file )
Parses the $file XML file and merges the types it defines in the
ConfigSpec defined by this parser.
=cut
sub merge_specification {
my ( $self, $file ) = @_;
check_param( $file, 'file' );
eval { $self->parsefile( $file ) };
croak "error while parsing XML Config spec $file: $@"
if $@;
return;
}
sub parse_start {
my $self = $_[0];
$self->init_stack( 'config_spec' );
$self->init_stack( 'config_value' );
return;
}
sub parse_end {
my $self = $_[0];
return $self->{'_spec'};
}
sub ignorable_ws {
my $self = $_[0];
$self->SUPER::dbk_element_char()
if $self->within_element( 'lrcsml:description' );
return;
}
sub config_spec_start {
my ( $self, $name, $attributes ) = @_;
$self->stack_push( 'config_spec', $self->{'_spec'} );
return;
}
sub spec_start {
my ( $self, $name, $attributes ) = @_;
$self->error( "No Lire::Config:: class defined for element '$name'" )
unless exists $elements2class{$name};
my $spec = eval {
no strict 'refs';
$elements2class{$name}->new( %$attributes );
};
$self->error( $@ ) if $@;
my $parent = $self->stack_peek( 'config_spec' );
$parent->add( $spec )
if defined $parent;
$self->stack_push( 'config_spec', $spec );
return;
}
sub spec_end {
my ( $self, $name ) = @_;
$self->stack_pop( 'config_spec' );
return;
}
sub current_param_spec {
my ( $self, $name ) = @_;
my $spec = $self->stack_peek( 'config_spec' );
return $spec->name() eq $name ? $spec : $spec->get( $name );
}
sub summary_end {
my ( $self, $name ) = @_;
my $spec = $self->stack_peek( 'config_spec' );
$spec->summary( $self->get_collector( 'lrcsml:summary' ) );
return;
}
sub description_end {
my ( $self, $name ) = @_;
my $spec = $self->stack_peek( 'config_spec' );
$spec->description( $self->dbk_string() );
return;
}
sub param_end {
my $self = $_[0];
my $spec = $self->stack_peek( 'config_spec' );
my $value = $self->stack_peek( 'config_value' );
$spec->default( $value )
if $self->stack_depth( 'config_value' ) == 1;
$self->SUPER::param_end();
return;
}
# keep perl happy
1;
__END__
=pod
=head1 SEE ALSO
Lire::Config::Parser(3pm), Lire::Config::TypeSpec(3pm),
Lire::Config::ConfigSpec(3pm)
=head1 AUTHORS
Wessel Dankers <wsl@logreport.org>
Francis J. Lacoste <flacoste@logreport.org>
=head1 VERSION
$Id: SpecParser.pm,v 1.44 2006/07/23 13:16:30 vanbaal Exp $
=head1 COPYRIGHT
Copyright (C) 2002,2004 Stichting LogReport Foundation LogReport@LogReport.org
This file is part of Lire.
Lire is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program (see COPYING); if not, check with
http://www.gnu.org/copyleft/gpl.html.
=cut
syntax highlighted by Code2HTML, v. 0.9.1