Go to the documentation of this file.00001 #!/usr/bin/perl
00002 #
00003 =head1 NAME
00004
00005 MMAgic.pl - Demonstrate use of various MMAgic packages.
00006
00007 =head1 SYNOPSIS
00008
00009 usage: MMAgic.pl <flag>* <file>
00010 <flag>
00011 --curse # cause endless recursion in process() function
00012 --show # show object parsed from input file before conversion
00013 --trace=<tag> # trace processing for SUBS and/or DETAIL
00014 # (can be used multiple times)
00015
00016 =head1 DESCRIPTION
00017
00018 Simple program to convert a configuration file in Perl data structure code
00019 into WDDX (XML data exchange format) format.
00020
00021 The real purpose of this script is to demonstrate the use of selected
00022 <tt>MMAgic</tt> libraries from my personal toolkit.
00023
00024 =over
00025
00026 =item MMAgic::Data
00027
00028 Loads data from a file that written in Perl data structure syntax.
00029 This is done by simply evaluating the contents of the file using the Perl interpreter.
00030 The <tt>Safe</tt> module is used to lock down the kinds of operations that the
00031 interpreter will parse, in an attempt to make this mechanism safe.
00032
00033 =item MMAgic::Log
00034
00035 Tagged log statements in a consistent format.
00036 Uses indentable output stream to show stack depth context of log statements.
00037 Provides data dump formats and subroutine information as required.
00038
00039 =item MMAgic::Output
00040
00041 Indentable output stream used by Log.pm.
00042 Also used to generate nicely formatted WDDX output in this example.
00043
00044 =item MMAgic::Trace
00045
00046 Filter-based trace mechanism.
00047 When disabled, trace statements are merely comments,
00048 so there is no run-time penalty.
00049
00050 =back
00051
00052 =head1 EXAMPLES
00053
00054 MMAgic.pl # usage message
00055 MMAgic.pl demo/book.data # working example
00056 MMAgic.pl demo/time.data # bad data file error
00057 MMAgic.pl demo/oops.data # non-existent data file
00058 MMAgic.pl demo/book.data --trace=SUBS # see trace statements
00059 MMAgic.pl demo/book.data --trace=SUBS --trace=DETAIL
00060 MMAgic.pl demo/book.data --trace=SUBS --curse # endless recursion
00061
00062 =head1 METHODS
00063
00064 =over
00065
00066 =cut
00067
00068 use strict;
00069 use warnings;
00070 use UNIVERSAL qw(isa);
00071
00072 use FindBin;
00073 use File::Basename;
00074 use Getopt::Long;
00075 use Pod::Usage;
00076
00077 use lib $FindBin::Bin;
00078
00079 use MMAgic::Data;
00080 use MMAgic::Log;
00081 use MMAgic::Trace;
00082
00083 our %option;
00084
00085 ###########################################################################
00086
00087 =item C<process($data, $stream)>
00088
00089 Recursively process the data object to generate WDDX output.
00090
00091 =cut
00092
00093 #=| \param $data Data structure from which to generate WDDX output.
00094 #=| \param $stream MMAgic::Output stream on which to generate WDDX output
00095
00096 sub process
00097 {
00098 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00099 # MMAgic Demo:
00100 # The Log->method() mechanism looks up the stack for the function name.
00101 # The block() call returns an indentation object that causes
00102 # automagic indentation of output log and pops indent when
00103 # the indentation object is destroyed at the end of the function.
00104 # Can't do that in Java!!!
00105 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00106 #!# my $auto = Log->method(@_)->block; #[SUBS]
00107 my $data = shift;
00108 my $stream = shift;
00109
00110 if (isa($data, 'HASH'))
00111 {
00112 #!# Log->trace('Found HASH object'); #[DETAIL]
00113 $stream->print("<struct>\n")->pushIndent;
00114
00115 map {
00116 $stream->print("<var name='$_'>\n")->pushIndent;
00117 process($data->{$_}, $stream);
00118 $stream->popIndent->print("</var>\n");
00119 } keys %$data;
00120
00121 $stream->popIndent->print("</struct>\n");
00122 }
00123
00124 elsif (isa($data, 'ARRAY'))
00125 {
00126 #!# Log->trace('Found ARRAY object'); #[DETAIL]
00127 $stream->print("<array length='", scalar(@$data), "'>\n")->pushIndent;
00128
00129 map {
00130 process($_, $stream);
00131 } @$data;
00132
00133 $stream->popIndent->print("</array>\n");
00134 }
00135
00136 elsif (ref($data))
00137 {
00138 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00139 # MMAgic Demo:
00140 # Not interpolating the object allows the Log mechanism
00141 # to handle various aberrant cases, such as undef:
00142 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00143 Log->warning('Unknown data object: ', $data);
00144 $stream->print("<string>Error converting: $data</string>\n");
00145 }
00146
00147 elsif (! defined($data))
00148 {
00149 #!# Log->trace('Found undefined object'); #[DETAIL]
00150 $stream->print("<null/>\n");
00151 }
00152
00153 elsif ($data =~ /[-+]?\d+(?:\.\d+)?/)
00154 {
00155 #!# Log->trace('Found numeric object'); #[DETAIL]
00156 $stream->print('<number>', $data, "</number>\n");
00157 }
00158
00159 else
00160 {
00161 # By default it must be a string (or printable as such):
00162 #!# Log->trace('Found string object'); #[DETAIL]
00163 $stream->print('<string>', $data, "</string>\n");
00164
00165 if ($option{curse})
00166 {
00167 # Simulate an infinite recursion error:
00168 process($data, $stream);
00169 }
00170 }
00171 }
00172
00173 ###########################################################################
00174 ###########################################################################
00175 #
00176 # Main program.
00177 #
00178
00179 # Parameter parsing using standard Perl packages:
00180
00181 pod2usage(2)
00182 unless GetOptions(\%option, qw(curse! show trace=s help));
00183
00184 pod2usage(1)
00185 if $option{help};
00186
00187 pod2usage('No files specified for conversion')
00188 unless @ARGV;
00189
00190 $option{file} = shift @ARGV;
00191
00192 Log->crash('Non-existent file ', $option{file})
00193 unless -f $option{file};
00194
00195 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00196 # MMAgic Demo:
00197 # Setup error logging...
00198 # MMAgic::Output provides an indentable output stream.
00199 # MMAgic::Log adds useful logging functions and a class-level interface.
00200 # It also places a timestamp and a log type on each line:
00201 #
00202 # 2002/05/13 19:06:30 [E] error message
00203 # 2002/05/13 19:06:30 [W] warning message
00204 # 2002/05/13 19:06:30 [T] trace statement
00205 # 2002/05/13 19:06:30 [I] informational message
00206 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00207
00208 Log->handle; # Initiate __WARN__ and __DIE__ handlers
00209 # which redirect to current Log.
00210
00211 ###########################################################################
00212
00213 Log->info('Starting ', $0);
00214
00215 my $data;
00216
00217 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00218 # MMAgic Demo:
00219 # Open input file as a MMAgic::Data object.
00220 # MMAgic::Data actually evaluates the file contents which are
00221 # in Perl data structure syntax within a Safe context to
00222 # prevent execution of unauthorized code.
00223 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00224
00225 tie $data, 'MMAgic::Data', $option{file};
00226
00227 Log->trace('Actual object:')->display($data)
00228 if $option{show};
00229
00230 my ($name, $path, $suff) = fileparse($option{file}, '\.[^\.]*');
00231
00232 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00233 # MMAgic Demo:
00234 # Open output file as MMAgic::Output stream which supports indentation.
00235 # Indentation supports makes it easier to generate readable WDDX output.
00236 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00237
00238 my $wddx = new MMAgic::Output(pathname => "$path$name.wddx");
00239
00240 # Generate the WDDX output:
00241
00242 $wddx->print(<<WDDX_HDR);
00243 <?xml version='1.0'?>
00244 <!DOCTYPE wddxPacket SYSTEM 'wddx_0100.dtd'>
00245
00246 <wddxPacket version='1.0'>
00247 <header/>
00248
00249 <data>
00250 WDDX_HDR
00251
00252 $wddx->pushIndent(4);
00253 process($data, $wddx);
00254 $wddx->popIndent;
00255
00256 $wddx->print(<<WDDX_FTR);
00257 </data>
00258 </wddxPacket>
00259 WDDX_FTR
00260
00261 Log->info('Finished ', $0);
00262
00263 __END__
00264
00265 =back
00266
00267 =head1 AUTHOR
00268
00269 Marc M. Adkins, L<mailto:Perl@Doorways.org>
00270
00271 =head1 COPYRIGHT AND LICENSE
00272
00273 Copyright 2004-2008 by Marc M. Adkins
00274
00275 =cut