File Coverage

blib/lib/SQL/Translator/Producer/XML/SQLFairy.pm
Criterion Covered Total %
statement 83 84 98.8
branch 20 26 76.9
condition 9 13 69.2
subroutine 9 9 100.0
pod 0 3 0.0
total 121 135 89.6


line stmt bran cond sub pod time code
1             package SQL::Translator::Producer::XML::SQLFairy;
2              
3             =pod
4              
5             =head1 NAME
6              
7             SQL::Translator::Producer::XML::SQLFairy - SQLFairy's default XML format
8              
9             =head1 SYNOPSIS
10              
11             use SQL::Translator;
12              
13             my $t = SQL::Translator->new(
14             from => 'MySQL',
15             to => 'XML-SQLFairy',
16             filename => 'schema.sql',
17             show_warnings => 1,
18             );
19              
20             print $t->translate;
21              
22             =head1 DESCRIPTION
23              
24             Creates XML output of a schema, in the flavor of XML used natively by the
25             SQLFairy project (L). This format is detailed here.
26              
27             The XML lives in the C namespace.
28             With a root element of .
29              
30             Objects in the schema are mapped to tags of the same name as the objects class
31             (all lowercase).
32              
33             The attributes of the objects (e.g. $field->name) are mapped to attributes of
34             the tag, except for sql, comments and action, which get mapped to child data
35             elements.
36              
37             List valued attributes (such as the list of fields in an index)
38             get mapped to comma separated lists of values in the attribute.
39              
40             Child objects, such as a tables fields, get mapped to child tags wrapped in a
41             set of container tags using the plural of their contained classes name.
42              
43             An objects' extra attribute (a hash of arbitrary data) is
44             mapped to a tag called extra, with the hash of data as attributes, sorted into
45             alphabetical order.
46              
47             e.g.
48              
49            
50             xmlns="http://sqlfairy.sourceforge.net/sqlfairy.xml">
51              
52            
53            
54            
55            
56             is_nullable="0" is_auto_increment="1" is_primary_key="1"
57             is_foreign_key="0" order="3">
58            
59            
60            
61            
62             is_nullable="1" is_auto_increment="0" is_primary_key="0"
63             is_foreign_key="0" order="1">
64            
65            
66            
67             ...
68            
69            
70            
71            
72            
73            
74              
75            
76            
77             SELECT email FROM Basic WHERE email IS NOT NULL
78            
79            
80              
81            
82              
83             To see a complete example of the XML translate one of your schema :)
84              
85             $ sqlt -f MySQL -t XML-SQLFairy schema.sql
86              
87             =head1 ARGS
88              
89             =over 4
90              
91             =item add_prefix
92              
93             Set to true to use the default namespace prefix of 'sqlf', instead of using
94             the default namespace for
95             C
96              
97             e.g.
98              
99            
100            
101              
102            
103            
104              
105             =item prefix
106              
107             Set to the namespace prefix you want to use for the
108             C
109              
110             e.g.
111              
112            
113            
114              
115             =item newlines
116              
117             If true (the default) inserts newlines around the XML, otherwise the schema is
118             written on one line.
119              
120             =item indent
121              
122             When using newlines the number of whitespace characters to use as the indent.
123             Default is 2, set to 0 to turn off indenting.
124              
125             =back
126              
127             =head1 LEGACY FORMAT
128              
129             The previous version of the SQLFairy XML allowed the attributes of the
130             schema objects to be written as either xml attributes or as data elements, in
131             any combination. The old producer could produce attribute only or data element
132             only versions. While this allowed for lots of flexibility in writing the XML
133             the result is a great many possible XML formats, not so good for DTD writing,
134             XPathing etc! So we have moved to a fixed version described above.
135              
136             This version of the producer will now only produce the new style XML.
137             To convert your old format files simply pass them through the translator :)
138              
139             $ sqlt -f XML-SQLFairy -t XML-SQLFairy schema-old.xml > schema-new.xml
140              
141             =cut
142              
143 5     5   31 use strict;
  5         10  
  5         124  
144 5     5   23 use warnings;
  5         14  
  5         192  
145             our @EXPORT_OK;
146             our $VERSION = '1.6_3';
147              
148 5     5   23 use Exporter;
  5         11  
  5         161  
149 5     5   24 use base qw(Exporter);
  5         19  
  5         653  
150             @EXPORT_OK = qw(produce);
151              
152 5     5   446 use SQL::Translator::Utils qw(header_comment debug);
  5         7  
  5         353  
153             BEGIN {
154             # Will someone fix XML::Writer already?
155 5     5   33 local $^W = 0;
156 5         2083 require XML::Writer;
157 5         28596 import XML::Writer;
158             }
159              
160             # Which schema object attributes (methods) to write as xml elements rather than
161             # as attributes. e.g. blah, blah...
162             my @MAP_AS_ELEMENTS = qw/sql comments action extra/;
163              
164              
165              
166             my $Namespace = 'http://sqlfairy.sourceforge.net/sqlfairy.xml';
167             my $Name = 'sqlf';
168             my $PArgs = {};
169             my $no_comments;
170              
171             sub produce {
172 10     10 0 21 my $translator = shift;
173 10         165 my $schema = $translator->schema;
174 10         201 $no_comments = $translator->no_comments;
175 10         209 $PArgs = $translator->producer_args;
176 10 50       181 my $newlines = defined $PArgs->{newlines} ? $PArgs->{newlines} : 1;
177 10 50       31 my $indent = defined $PArgs->{indent} ? $PArgs->{indent} : 2;
178              
179             # Setup the XML::Writer and set the namespace
180 10         16 my $io;
181 10         18 my $prefix = "";
182 10 50       29 $prefix = $Name if $PArgs->{add_prefix};
183 10 50       26 $prefix = $PArgs->{prefix} if $PArgs->{prefix};
184 10         83 my $xml = XML::Writer->new(
185             OUTPUT => \$io,
186             NAMESPACES => 1,
187             PREFIX_MAP => { $Namespace => $prefix },
188             DATA_MODE => $newlines,
189             DATA_INDENT => $indent,
190             );
191              
192             # Start the document
193 10         2861 $xml->xmlDecl('UTF-8');
194              
195 10 100       491 $xml->comment(header_comment('', ''))
196             unless $no_comments;
197              
198 10         243 xml_obj($xml, $schema,
199             tag => "schema", methods => [qw/name database extra/], end_tag => 0 );
200              
201             #
202             # Table
203             #
204 10         46 $xml->startTag( [ $Namespace => "tables" ] );
205 10         1083 for my $table ( $schema->get_tables ) {
206 29         1340 debug "Table:",$table->name;
207 29         123 xml_obj($xml, $table,
208             tag => "table",
209             methods => [qw/name order extra/],
210             end_tag => 0
211             );
212              
213             #
214             # Fields
215             #
216 29         144 xml_obj_children( $xml, $table,
217             tag => 'field',
218             methods =>[qw/
219             name data_type size is_nullable default_value is_auto_increment
220             is_primary_key is_foreign_key extra comments order
221             /],
222             );
223              
224             #
225             # Indices
226             #
227 29         1260 xml_obj_children( $xml, $table,
228             tag => 'index',
229             collection_tag => "indices",
230             methods => [qw/name type fields options extra/],
231             );
232              
233             #
234             # Constraints
235             #
236 29         1213 xml_obj_children( $xml, $table,
237             tag => 'constraint',
238             methods => [qw/
239             name type fields reference_table reference_fields
240             on_delete on_update match_type expression options deferrable
241             extra
242             /],
243             );
244              
245             #
246             # Comments
247             #
248 29         1206 xml_obj_children( $xml, $table,
249             tag => 'comment',
250             collection_tag => "comments",
251             methods => [qw/
252             comments
253             /],
254             );
255              
256 29         1085 $xml->endTag( [ $Namespace => 'table' ] );
257             }
258 10         353 $xml->endTag( [ $Namespace => 'tables' ] );
259              
260             #
261             # Views
262             #
263 10         420 xml_obj_children( $xml, $schema,
264             tag => 'view',
265             methods => [qw/name sql fields order extra/],
266             );
267              
268             #
269             # Tiggers
270             #
271 10         416 xml_obj_children( $xml, $schema,
272             tag => 'trigger',
273             methods => [qw/name database_events action on_table perform_action_when
274             fields order extra scope/],
275             );
276              
277             #
278             # Procedures
279             #
280 10         415 xml_obj_children( $xml, $schema,
281             tag => 'procedure',
282             methods => [qw/name sql parameters owner comments order extra/],
283             );
284              
285 10         411 $xml->endTag([ $Namespace => 'schema' ]);
286 10         431 $xml->end;
287              
288 10         1508 return $io;
289             }
290              
291              
292             #
293             # Takes and XML::Write object, Schema::* parent object, the tag name,
294             # the collection name and a list of methods (of the children) to write as XML.
295             # The collection name defaults to the name with an s on the end and is used to
296             # work out the method to get the children with. eg a name of 'foo' gives a
297             # collection of foos and gets the members using ->get_foos.
298             #
299             sub xml_obj_children {
300 146     146 0 263 my ($xml,$parent) = (shift,shift);
301 146         382 my %args = @_;
302             my ($name,$collection_name,$methods)
303 146         323 = @args{qw/tag collection_tag methods/};
304 146   66     448 $collection_name ||= "${name}s";
305              
306 146         176 my $meth;
307 146 100       297 if ( $collection_name eq 'comments' ) {
308 29         40 $meth = 'comments';
309             } else {
310 117         162 $meth = "get_$collection_name";
311             }
312              
313 146         1050 my @kids = $parent->$meth;
314             #@kids || return;
315 146         721 $xml->startTag( [ $Namespace => $collection_name ] );
316              
317 146         15821 for my $obj ( @kids ) {
318 202 50       6078 if ( $collection_name eq 'comments' ){
319 0         0 $xml->dataElement( [ $Namespace => 'comment' ], $obj );
320             } else {
321 202         522 xml_obj($xml, $obj,
322             tag => "$name",
323             end_tag => 1,
324             methods => $methods,
325             );
326             }
327             }
328 146         3708 $xml->endTag( [ $Namespace => $collection_name ] );
329             }
330              
331             #
332             # Takes an XML::Writer, Schema::* object and list of method names
333             # and writes the object out as XML. All methods values are written as attributes
334             # except for the methods listed in @MAP_AS_ELEMENTS which get written as child
335             # data elements.
336             #
337             # The attributes/tags are written in the same order as the method names are
338             # passed.
339             #
340             # TODO
341             # - Should the Namespace be passed in instead of global? Pass in the same
342             # as Writer ie [ NS => TAGNAME ]
343             #
344             my $elements_re = join("|", @MAP_AS_ELEMENTS);
345             $elements_re = qr/^($elements_re)$/;
346             sub xml_obj {
347 241     241 0 814 my ($xml, $obj, %args) = @_;
348 241   50     596 my $tag = $args{'tag'} || '';
349 241   100     582 my $end_tag = $args{'end_tag'} || '';
350 241         314 my @meths = @{ $args{'methods'} };
  241         610  
351 241         371 my $empty_tag = 0;
352              
353             # Use array to ensure consistent (ie not hash) ordering of attribs
354             # The order comes from the meths list passed in.
355 241         332 my @tags;
356             my @attr;
357 241         422 foreach ( grep { defined $obj->$_ } @meths ) {
  2205         44613  
358 2130 100       7424 my $what = m/$elements_re/ ? \@tags : \@attr;
359 2130 100       27934 my $val = $_ eq 'extra'
360             ? { $obj->$_ }
361             : $obj->$_;
362 2130 100       13442 $val = ref $val eq 'ARRAY' ? join(',', @$val) : $val;
363 2130         4624 push @$what, $_ => $val;
364             };
365 241         389 my $child_tags = @tags;
366 241 50 66     1586 $end_tag && !$child_tags
367             ? $xml->emptyTag( [ $Namespace => $tag ], @attr )
368             : $xml->startTag( [ $Namespace => $tag ], @attr );
369 241         90020 while ( my ($name,$val) = splice @tags,0,2 ) {
370 401 100       20931 if ( ref $val eq 'HASH' ) {
371             $xml->emptyTag( [ $Namespace => $name ],
372 241         1094 map { ($_, $val->{$_}) } sort keys %$val );
  116         306  
373             }
374             else {
375 160         561 $xml->dataElement( [ $Namespace => $name ], $val );
376             }
377             }
378 241 100 66     38600 $xml->endTag( [ $Namespace => $tag ] ) if $child_tags && $end_tag;
379             }
380              
381             1;
382              
383             # -------------------------------------------------------------------
384             # The eyes of fire, the nostrils of air,
385             # The mouth of water, the beard of earth.
386             # William Blake
387             # -------------------------------------------------------------------
388              
389             =pod
390              
391             =head1 AUTHORS
392              
393             Ken Youens-Clark Ekclark@cpan.orgE,
394             Darren Chamberlain Edarren@cpan.orgE,
395             Mark Addison Emark.addison@itn.co.ukE.
396              
397             =head1 SEE ALSO
398              
399             C, L, L,
400             L, L.
401              
402             =cut