1###############################################################################
2#
3# This file copyright (c) 2001-2011 Randy J. Ray, all rights reserved
4#
5# Copying and distribution are permitted under the terms of the Artistic
6# License 2.0 (http://www.opensource.org/licenses/artistic-license-2.0.php) or
7# the GNU LGPL (http://www.opensource.org/licenses/lgpl-2.1.php).
8#
9###############################################################################
10#
11#   Description:    This is the RPC::XML::ParserFactory class, a factory for
12#                   classes that derive from the RPC::XML::Parser class.
13#
14#   Functions:      import
15#                   new
16#                   register
17#
18#   Libraries:      RPC::XML::Parser::XMLParser \
19#                   RPC::XML::Parser::XMLLibXML  > One (or more) of these
20#                   RPC::XML::Parser::XMLSAX    /
21#
22#   Global Consts:  $VERSION
23#
24#   Environment:    None.
25#
26###############################################################################
27
28package RPC::XML::ParserFactory;
29
30use 5.008008;
31use strict;
32use warnings;
33use vars qw($VERSION %AVAILABLE $PARSER_CLASS);
34use subs qw(import new register);
35
36# Because this is a factory class, there are some eval's that violate this
37# critic policy, but can't be worked around:
38## no critic (RequireCheckingReturnValueOfEval)
39
40$VERSION = '1.03';
41$VERSION = eval $VERSION; ## no critic (ProhibitStringyEval)
42
43# These are the known parsers supported, not including any that are specified
44# by the user at import-time.
45$PARSER_CLASS = 'XML::Parser';
46%AVAILABLE    = (
47    'XML::Parser' => 'RPC::XML::Parser::XMLParser',
48    'XML::LibXML' => 'RPC::XML::Parser::XMLLibXML',
49);
50
51# "Normalize" the key-names to allow some simplicity (and sugar):
52for (keys %AVAILABLE)
53{
54    my $key = lc $_;
55    $AVAILABLE{$key} = $AVAILABLE{$_};
56    $key =~ s/:://g;
57    $AVAILABLE{$key} = $AVAILABLE{$_};
58}
59
60###############################################################################
61#
62#   Sub Name:       import
63#
64#   Description:    Method called when this module is use'd
65#
66#   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
67#                   $class    in      scalar    Class name (not used)
68#                   @args     in      list      Arguments to the import
69#
70#   Globals:        $PARSER_CLASS
71#
72#   Returns:        void
73#
74###############################################################################
75sub import
76{
77    my (undef, @args) = @_;
78
79    # As a special-case, this one parameter might be specified without the
80    # key, if it is the ONLY thing passed:
81    if (1 == @args)
82    {
83        @args = (class => @args);
84    }
85
86    # For now, the only arguments are key/value pairs so it's safe to coerce
87    # this into a hash
88    my %argz = @args;
89
90    # In fact, for now, this is the only argument:
91    if ($argz{class})
92    {
93        $PARSER_CLASS = $argz{class};
94    }
95
96    return;
97}
98
99###############################################################################
100#
101#   Sub Name:       new
102#
103#   Description:    Constructor. Save any important attributes, leave the
104#                   heavy lifting for the parse() routine and XML::Parser.
105#
106#   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
107#                   $class    in      scalar    Class we're initializing
108#                   %attr     in      hash      Any extras the caller wants
109#
110#   Globals:        $RPC::XML::ERROR
111#
112#   Returns:        Success:    object ref
113#                   Failure:    undef
114#
115###############################################################################
116sub new
117{
118    my ($class, %attrs) = @_;
119
120    my $factory = delete $attrs{class} || $PARSER_CLASS;
121
122    if ($class = $AVAILABLE{$factory})
123    {
124        eval "require $class;"; ## no critic (ProhibitStringyEval)
125        if ($@)
126        {
127            $RPC::XML::ERROR = __PACKAGE__ . "::new: Error loading $class (" .
128              "factory for '$factory'): $@";
129            return;
130        }
131    }
132    else
133    {
134        # This means that the class is not one of the built-in ones. Try to
135        # load it, then make sure it's a sub-class of this one:
136        $class = $factory;
137        eval "require $class;"; ## no critic (ProhibitStringyEval)
138        if ($@)
139        {
140            $RPC::XML::ERROR = __PACKAGE__ . "::new: Error loading $class: $@";
141            return;
142        }
143        # Loaded OK... is it a descendent?
144        if  (! $class->isa(__PACKAGE__))
145        {
146            $RPC::XML::ERROR = __PACKAGE__ . "::new: Class '$class' cannot " .
147              'be used, as it is not a sub-class of ' . __PACKAGE__;
148            return;
149        }
150    }
151
152    return $class->new(%attrs);
153}
154
1551;
156
157__END__
158
159=head1 NAME
160
161RPC::XML::ParserFactory - A factory class for RPC::XML::Parser objects
162
163=head1 SYNOPSIS
164
165    use RPC::XML::ParserFactory;
166    ...
167    $P = RPC::XML::ParserFactory->new();
168    $P->parse($message);
169
170=head1 DESCRIPTION
171
172The B<RPC::XML::ParserFactory> class encapsulates the process of creating
173parser objects that adhere to the interface described in
174L<RPC::XML::Parser|RPC::XML::Parser>.  Under the hood, the parser object
175created and returned could be from any of a number of implementation classes.
176
177=head1 IMPORT-TIME ARGUMENTS
178
179You can specify a particular underlying parser class to use, if you do not
180want B<RPC::XML::ParserFactory> to use the default class. This is done with
181the C<class> keyword:
182
183    use RPC::XML::ParserFactory (class => 'XML::Parser');
184
185The value may be the name for any of the built-in classes, or it may be the
186name of a class that inherits from B<RPC::XML::Parser> (and can thus be
187"manufactured" by the factory). The value is saved and becomes the default
188class for any calls to B<new> that do not explicitly name a class to use.
189
190Note that if the specified class is not valid, this is not tested until the
191first call to B<new>, at which point an invalid class will cause an exception
192(error) to occur. The constructor will return C<undef> and the
193B<$RPC::XML::ERROR> variable will contain the error message.
194
195=head2 Names of Built-In Parsers
196
197The following names are valid when specified as the value of the C<class>
198argument described above:
199
200=over 4
201
202=item XML::Parser
203
204=item xml::parser
205
206=item xmlparser
207
208All of these specify the parser implementation based on the B<XML::Parser>
209module. This is the default parser if the user does not specify any
210alternative.
211
212=item XML::LibXML
213
214=item xml::libxml
215
216=item xmllibxml
217
218These specify a parser implementation based on the B<XML::LibXML> module.
219This is a new parser and not as well-vetted as the previous one, hence it
220must be explicitly requested.
221
222=back
223
224=head1 SUBROUTINES/METHODS
225
226The methods are:
227
228=over 4
229
230=item new([ARGS])
231
232Create a new instance of the class. Any extra data passed to the constructor
233is taken as key/value pairs (B<not> a hash reference) and attached to the
234object.
235
236This method passes all arguments on to the new() method of the chosen
237implementation class, except for the following:
238
239=over 4
240
241=item class NAME
242
243If the user chooses, they may specify an explicit class to use for parsers
244when calling new(). If passed, this overrides any value that was given at
245use-time (processed by import()).
246
247=back
248
249=back
250
251=head1 DIAGNOSTICS
252
253The constructor returns C<undef> upon failure, with the error message available
254in the global variable B<C<$RPC::XML::ERROR>>.
255
256=head1 BUGS
257
258Please report any bugs or feature requests to
259C<bug-rpc-xml at rt.cpan.org>, or through the web interface at
260L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=RPC-XML>. I will be
261notified, and then you'll automatically be notified of progress on
262your bug as I make changes.
263
264=head1 SUPPORT
265
266=over 4
267
268=item * RT: CPAN's request tracker
269
270L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=RPC-XML>
271
272=item * AnnoCPAN: Annotated CPAN documentation
273
274L<http://annocpan.org/dist/RPC-XML>
275
276=item * CPAN Ratings
277
278L<http://cpanratings.perl.org/d/RPC-XML>
279
280=item * Search CPAN
281
282L<http://search.cpan.org/dist/RPC-XML>
283
284=item * MetaCPAN
285
286L<https://metacpan.org/release/RPC-XML>
287
288=item * Source code on GitHub
289
290L<http://github.com/rjray/rpc-xml>
291
292=back
293
294=head1 LICENSE AND COPYRIGHT
295
296This file and the code within are copyright (c) 2011 by Randy J. Ray.
297
298Copying and distribution are permitted under the terms of the Artistic
299License 2.0 (L<http://www.opensource.org/licenses/artistic-license-2.0.php>) or
300the GNU LGPL 2.1 (L<http://www.opensource.org/licenses/lgpl-2.1.php>).
301
302=head1 CREDITS
303
304The B<XML-RPC> standard is Copyright (c) 1998-2001, UserLand Software, Inc.
305See L<http://www.xmlrpc.com> for more information about the B<XML-RPC>
306specification.
307
308=head1 SEE ALSO
309
310L<RPC::XML|RPC::XML>, L<RPC::XML::Client|RPC::XML::Client>,
311L<RPC::XML::Server|RPC::XML::Server>, L<XML::Parser|XML::Parser>
312
313=head1 AUTHOR
314
315Randy J. Ray C<< <rjray@blackperl.com> >>
316
317=cut
318