File Coverage

blib/lib/XML/SAX/Machines.pm
Criterion Covered Total %
statement 83 87 95.4
branch 16 24 66.6
condition 8 16 50.0
subroutine 16 16 100.0
pod 0 5 0.0
total 123 148 83.1


line stmt bran cond sub pod time code
1             package XML::SAX::Machines;
2             {
3             $XML::SAX::Machines::VERSION = '0.46';
4             }
5              
6             # ABSTRACT: manage collections of SAX processors
7              
8              
9 14     14   129513 use strict;
  14         34  
  14         533  
10 14     14   82 use Carp;
  14         26  
  14         871  
11 14     14   72 use Exporter;
  14         23  
  14         485  
12 14     14   66 use vars qw( $debug @ISA @EXPORT_OK %EXPORT_TAGS );
  14         25  
  14         6291  
13              
14             ## TODO: Load this mapping from the config file, or generalize
15             ## this.
16             my %machines = (
17             ByRecord => "XML::SAX::ByRecord",
18             Machine => "XML::SAX::Machine",
19             Manifold => "XML::SAX::Manifold",
20             Pipeline => "XML::SAX::Pipeline",
21             Tap => "XML::SAX::Tap",
22             );
23              
24             @ISA = qw( Exporter );
25             @EXPORT_OK = keys %machines;
26             %EXPORT_TAGS = ( "all" => \@EXPORT_OK );
27              
28             ## Note: we don't put a constructor function in each package for two reasons.
29             ## The first is that I want to generalize this mechanism in to a
30             ## Class::CtorShortcut. The second, more marginal reason is that the
31             ## easiest way to do that
32             ## would be to make each of the machines be @ISA( Exporter ) and I don't
33             ## want to add to to machines' @ISA lists for speed reasons, since
34             ## below we manually search @ISA hierarchies for config settings.
35             sub import {
36 27     27   149 my $self = $_[0];
37 27         145 for ( @_[1..$#_] ) {
38 15 100       98 for ( substr( $_, 0, 1 ) eq ":" ? @{$EXPORT_TAGS{substr $_, 1}} : $_ ) {
  1         5  
39 19 50       95 croak "Unknown SAX machine: '$_'" unless exists $machines{$_};
40 19 50       180 carp "Loading SAX machine '$_'" if $debug;
41 19 50   14 0 1378 eval "use $machines{$_}; sub $_ { $machines{$_}->new( \@_ ) }; 1;"
  12     4 0 9744  
  12     1 0 41  
  12     1   275  
  4     1   13416  
  4         11  
  4         41  
  1         552  
  1         2  
  1         7  
  1         599  
  1         2  
  1         33  
  1         5  
  1         2  
  1         7  
42             or die $@;
43             }
44             }
45              
46 27         34462 goto &Exporter::import;
47             }
48              
49              
50             sub _read_config {
51 16     16   2745 delete $INC{"XML/SAX/Machines/ConfigDefaults.pm"};
52 30         10144 delete $INC{"XML/SAX/Machines/SiteConfig.pm"};
53              
54 16         3090 eval "require XML::SAX::Machines::ConfigDefaults;";
55 23         3175 eval "require XML::SAX::Machines::SiteConfig;";
56              
57 16         1883 my $xsm = "XML::SAX::Machines";
58              
59 14         50 for ( qw(
60             LegalProcessorClassOptions
61             ProcessorClassOptions
62             ) ) {
63 14     14   88 no strict "refs";
  14         26  
  14         9894  
64            
65             ## I don't like creating these just to default them, but perls
66             ## 5.005003 and older (at least) emit a "used only once, possible
67             ## type" warngings that local $^W = 0 doesn't silence.
68 28   50     43 ${__PACKAGE__."::ConfigDefaults::$_"} ||= {};
  28         216  
69 28   50     39 ${__PACKAGE__."::SiteConfig::$_"} ||= {};
  28         288  
70 28         141 ${__PACKAGE__."::Config::$_"} = {
  28         159  
71 28         40 %{ ${__PACKAGE__."::ConfigDefaults::$_"} },
  28         273  
72 28         36 %{ ${__PACKAGE__."::SiteConfig::$_" } },
  28         41  
73             };
74             }
75              
76             ## Now check the config.
77 14         31 my @errors;
78 14         52 for my $class ( keys %$XML::SAX::Machines::Config::ProcessorClassOptions ) {
79 56         685 push(
80             @errors,
81             "Illegal ProcessorClassOptions option name in $class: '$_'\n"
82 56         202 ) for grep(
83             ! exists $XML::SAX::Machines::Config::LegalProcessorClassOptions->{$_},
84             keys %{$XML::SAX::Machines::Config::ProcessorClassOptions->{$class}}
85             ) ;
86             }
87              
88 14 50       94 die @errors,
89             " check XML::SAX::Machines::SiteConfig",
90             " (or perhaps XML::SAX::Machines::ConfigDefaults)\n",
91             " Legal names are: ",
92             join(
93             ", ",
94             map
95             "'$_'",
96             keys %$XML::SAX::Machines::Config::LegalProcessorClassOptions
97             )
98             if @errors;
99             }
100              
101             _read_config;
102              
103              
104             sub _config_as_string {
105 0     16   0 require Data::Dumper;
106 0         0 local $Data::Dumper::Indent = 1;
107 0         0 local $Data::Dumper::QuoteKeys = 1;
108 0         0 Data::Dumper->Dump(
109             [ $XML::SAX::Machines::Config::ProcessorClassOptions ],
110             [ 'Processors' ]
111             );
112             }
113              
114             ## TODO: Move the config file accessors to a Config package.
115             #=head2 Config File accessors
116             #
117             #Right now config files are read only.
118             #
119             #=cut
120             #
121             #=over
122             #
123             #=item processor_class_option
124             #
125             # if ( XML::SAX::Machines->processor_class_option
126             # $class, "ConstructWithHashedOptions"
127             # ) {
128             # ....
129             # }
130             ##
131             #Sees if an option is set for a processor class or the first class in it's
132             #ISA hierarchy for which the option is defined. Caches results for speed.
133             #The cache is cleared if the config file is re-read.
134             #
135             #$class may also be an object.
136             #
137             #Yes this is a wordy API; it shouldn't be needed too often :).
138             #
139             #=cut
140             #
141             sub processor_class_option {
142 65     67 0 681 my $self = shift;
143 65         110 my ( $class, $option ) = @_;
144              
145 65 50       166 croak "Can't set processor class options yet"
146             if @_ > 2;
147              
148 65 50       195 Carp::cluck
149             "Unknown ProcessorClassOptions option '$option'.\n",
150             " Expected options are: ",
151             join(
152             ", ",
153             map "'$_'",
154             sort keys
155             %$XML::SAX::Machines::Config::ExpectedProcessorClassOptions
156             ),
157             "\n",
158             " Perhaps a call to XML::SAX::Machine->expected_processor_class_options( '$option' ) would help?"
159             unless
160             $XML::SAX::Machines::Config::ExpectedProcessorClassOptions->{$option};
161              
162 65   33     250 $class = ref $class || $class;
163              
164 65 50 66     351 return $XML::SAX::Machines::Config::ProcessorClassOptions->{$class}->{$option}
      66        
165             if exists $XML::SAX::Machines::Config::ProcessorClassOptions->{$class}
166             && exists $XML::SAX::Machines::Config::ProcessorClassOptions->{$class}->{$option}
167             && defined $XML::SAX::Machines::Config::ProcessorClassOptions->{$class}->{$option};
168              
169             ## Hmm, gotta traipse through @ISA.
170 48         63 my $isa = do {
171 14     14   90 no strict "refs";
  14         27  
  14         4004  
172 48 100       69 eval "require $class;" unless @{"${class}::ISA"};
  48         2220  
173 48         104 \@{"${class}::ISA"};
  48         178  
174             };
175              
176 48         58 my $value;
177 48         116 for ( @$isa ) {
178 16 50 33     99 next if $_ eq "Exporter" || $_ eq "DynaLoader" ;
179 16         78 $value = $self->processor_class_option( $_, $option );
180 16 100       61 last if defined $value;
181             }
182              
183 48 100       230 return undef unless $value;
184              
185             ## Cache the result.
186 2         7 $XML::SAX::Machines::Config::ProcessorClassOptions->{$class}->{$option}
187             = $value;
188 2         8 return $value;
189             }
190              
191             #=item expected_processor_class_options
192             #
193             # XML::SAX::Machine->expected_processor_class_options( MyOption );
194             #
195             #This is used to inform XML::SAX::Machines that there's an option your
196             #module expects to be able to retrieve. It does *not* check the options
197             #in the config file, it checks options requests so as to catch typoes in
198             #code.
199             #
200             #Yes this is a wordy API; it shouldn't be needed too often :).
201             #
202             #=cut
203              
204             sub expected_processor_class_options {
205 14     23 0 39 my $self = shift;
206              
207             $XML::SAX::Machines::Config::ExpectedProcessorClassOptions->{$_} = 1
208 14         118 for @_;
209             }
210              
211             #=back
212             #
213             #=cut
214              
215             1;
216              
217             __END__