File Coverage

blib/lib/DBIx/XMLServer.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # $Id: XMLServer.pm,v 1.19 2005/11/15 22:03:01 mjb47 Exp $
2              
3 17     17   486730 use strict;
  17         46  
  17         1452  
4 17     17   97 use warnings;
  17         30  
  17         509  
5 17     17   43394 use XML::LibXML;
  0            
  0            
6             use XML::LibXSLT;
7              
8             package DBIx::XMLServer;
9              
10             our $VERSION = '0.02';
11              
12             my $our_ns = 'http://boojum.org.uk/NS/XMLServer';
13              
14             my $sql_ns = sub {
15             my $node = shift;
16             my $uri = shift || $our_ns;
17             my $prefix;
18             $prefix = $node->lookupNamespacePrefix($uri) and return $prefix;
19             for($prefix = 'a'; $node->lookupNamespaceURI($prefix); ++$prefix) {}
20             $node->setNamespace($uri, $prefix, 0);
21             return $prefix;
22             };
23              
24             package DBIx::XMLServer::Field;
25             use Carp;
26              
27             our $VERSION = sprintf '%d.%03d', (q$Revision: 1.19 $ =~ /(\d+)\.(\d+)/);
28              
29             sub new {
30             my $proto = shift;
31             my $class = ref($proto) || $proto;
32             my $self = {};
33             $self->{XMLServer} = shift
34             and ref $self->{XMLServer}
35             and $self->{XMLServer}->isa('DBIx::XMLServer')
36             or croak "No XMLServer object supplied";
37             $self->{node} = shift
38             and ref $self->{node}
39             and $self->{node}->isa('XML::LibXML::Element')
40             or croak "No XML element node supplied";
41             $self->{node}->namespaceURI eq $our_ns
42             and $self->{node}->localname eq 'field'
43             or croak "The node is not an element";
44             my $type = $self->{node}->getAttribute('type')
45             or croak " element has no `type' attribute";
46             $class = $self->{XMLServer}->{types}->{$type}
47             or croak "Undefined field type: `$type'";
48             bless($self, $class);
49             $self->init if $self->can('init');
50             return $self;
51             }
52              
53             sub where { return '1'; }
54              
55             sub select {
56             my $self = shift;
57             my $expr = $self->{node}->getAttribute('expr')
58             or die "A element has no `expr' attribute";
59             return $expr;
60             }
61              
62             sub join {
63             my $self = shift;
64             return $self->{node}->getAttribute('join');
65             }
66              
67             sub value {
68             my $self = shift;
69             return shift @{shift()};
70             }
71              
72             sub result {
73             my $self = shift;
74             my $n = shift;
75              
76             my $value = $self->value(shift());
77              
78             do {
79             $value = $n->ownerDocument->createElementNS($our_ns, 'sql:null');
80             $value->setAttribute('type',
81             $self->{node}->getAttribute('null') || 'empty');
82             } unless defined $value;
83              
84             do {
85             my $x = $n->ownerDocument->createTextNode($value);
86             $value = $x;
87             } unless ref $value;
88              
89             my $attr = $self->{node}->getAttribute('attribute');
90              
91             if($attr) {
92             my $x = $n->ownerDocument->createElementNS($our_ns, 'sql:attribute');
93             $x->setAttribute('name', $attr);
94             $x->appendChild($value);
95             $value = $x;
96             }
97              
98             $n->replaceNode($value);
99             }
100              
101             1;
102              
103             package DBIx::XMLServer::OrderSpec;
104              
105             our $VERSION = sprintf '%d.%03d', (q$Revision: 1.19 $ =~ /(\d+)\.(\d+)/);
106              
107             sub new {
108             my $proto = shift;
109             my $class = ref($proto) || $proto;
110             my $self = {};
111              
112             my ($xmlserver, $node, $dir) = @_;
113             $self->{field} = new DBIx::XMLServer::Field($xmlserver, $node);
114             $self->{dir} = $dir;
115              
116             bless($self, $class);
117             return $self;
118             }
119              
120             sub orderspec {
121             my $self = shift;
122             my $spec = $self->{field}->select;
123             for ($self->{dir}) {
124             defined $_ or last;
125             /^ascending$/ && do {
126             $spec .= ' ASC';
127             last;
128             };
129             /^descending$/ && do {
130             $spec .= ' DESC';
131             last;
132             };
133             }
134             return $spec;
135             }
136              
137             1;
138              
139             package DBIx::XMLServer::Request;
140             use Carp;
141             use Text::Balanced qw(extract_bracketed);
142              
143             our $VERSION = sprintf '%d.%03d', (q$Revision: 1.19 $ =~ /(\d+)\.(\d+)/);
144              
145             if ($ lt v5.7)
146             {
147             require Exporter;
148             our @ISA = qw(Exporter);
149             our @EXPORT = qw(IsNCNameStartChar IsNCNameChar);
150             }
151              
152             # Look for an initial segment of the string which looks like an XPath
153             # pattern
154             sub get_xpath {
155             my $text = shift;
156              
157             # Repeatedly skip XPath-like stuff and bracketed things
158             while( (extract_bracketed($text, "[(\"'",
159             '[-|@_./:[:alnum:][:space:]]*')) [0]) {};
160             # Skip any more XPath-like stuff
161             $text =~ m'\G[-|@_./:[:alnum:][:space:]]*'g;
162              
163             return substr($text, 0, pos $text), substr($text, pos $text);
164             }
165              
166             BEGIN {
167             # This hack is because Perl 5.6.1 appears to be buggy and not
168             # allow unicode character properties to be declared in a package
169             # pther than main.
170             our $property_package = $ lt v5.8 ? 'main' : 'DBIx::XMLServer::Request';
171             eval <
172             package $property_package;
173              
174             # These are the ranges defined by XML 1.1, as these
175             # are more up-to-date w.r.t Unicode those defined by
176             # XML 1.0 (3rd ed). They're also much simpler to specify.
177             sub IsNCNameStartChar {
178             return <
179             41 5A
180             5F
181             61 7A
182             C0 D6
183             D8 F6
184             F8 2FF
185             370 37D
186             37F 1FFF
187             200C 200D
188             2070 218F
189             2C00 2FEF
190             3001 D7FF
191             F900 FDCF
192             FDF0 FFFD
193             10000 EFFFF
194             END
195             }
196              
197             sub IsNCNameChar {
198             return <
199             2D 2E
200             30 39
201             41 5A
202             5F
203             61 7A
204             B7
205             C0 D6
206             D8 F6
207             F8 2FF
208             300 36F
209             370 37D
210             37F 1FFF
211             200C 200D
212             203F 2040
213             2070 218F
214             2C00 2FEF
215             3001 D7FF
216             F900 FDCF
217             FDF0 FFFD
218             10000 EFFFF
219             END
220             }
221              
222             END_PROPERTIES
223             }
224              
225             # Definition of NCName as per XML Namespaces 1.1
226             use utf8;
227             our $NCName = qr/(?:\p{IsNCNameStartChar}\p{IsNCNameChar}*)/;
228              
229             sub add_prefix($$) {
230              
231             my ( $xpath, $prefix ) = @_;
232              
233             while ( $xpath =~ s/
234             ^( (?:[^'"]*(?:"[^"]*"|'[^']*'))*[^'"]*
235             (?: (?
236             | (?: (?: (?<=[\@([,\/|+-=<>])
237             | (?<=[<>!]=|\/\/|::)
238             | ^)
239             (?: $NCName\s+ $NCName\s+ )*
240             | (?: (?<=[.\])"'])
241             | [0-9]+(?:\.[0-9]+)?\s
242             | \$$NCName(?::$NCName)?\s )
243             \s* $NCName\s+
244             (?: $NCName\s+ $NCName\s+ )* )
245             (?
246             ($NCName)
247             (\s+[^:(\s]|\s*(?![:(\s])\P{IsNCNameChar}|$)
248             /$1$prefix:$2$3/x ) {}
249              
250             return $xpath;
251             }
252              
253             sub new {
254             my $proto = shift;
255             my $class = ref($proto) || $proto;
256             my $self;
257             if($#_ <= 1) {
258             $self = {};
259             $self->{XMLServer} = shift;
260             $self->{template} = shift;
261             } else {
262             $self = { @_ };
263             };
264             ref $self->{XMLServer}
265             and $self->{XMLServer}->isa('DBIx::XMLServer')
266             or croak "No XMLServer object supplied";
267              
268             $self->{template} or $self->{template} = $self->{XMLServer}->{template};
269             $self->{template}->isa('XML::LibXML::Element')
270             or croak "Template is not a XML::LibXML::Element";
271              
272             $self->{template}->localname eq 'template'
273             && $self->{template}->namespaceURI eq $our_ns
274             or croak "Template is not ";
275             $self->{main_table} = $self->{template}->getAttribute('table')
276             or croak "The element has no `table' attribute";
277             $self->{ns} = $self->{template}->getAttribute('default-namespace');
278             my $p = &$sql_ns($self->{template});
279             $self->{record} = $self->{template}->findnodes(".//$p:record/*[1]")->shift
280             or croak "The element contains no element";
281              
282             $self->{criteria} = [];
283             $self->{page} = 0;
284             $self->{pagesize} = $self->{XMLServer}->{maxpagesize}
285             unless defined $self->{pagesize};
286             $self->{rowcount} = $self->{XMLServer}->{rowcount}
287             unless defined $self->{rowcount};
288             bless($self, $class);
289             return $self;
290             }
291              
292             sub real_parse {
293             my $self = shift;
294             my $query = shift or croak "No query string supplied";
295             foreach(split /&/, $query) {
296             # Un-URL-encode the string
297             tr/+/ /;
298             s/%([0-9A-Fa-f][0-9A-Fa-f])/chr(hex($1))/eg;
299             # Split it into key and condition
300             my ($key, $condition) = get_xpath($_);
301             $key or return "Unrecognised condition: '$condition'";
302             for ($key) {
303             /^fields$/ && do {
304             $condition =~ s/^=//
305             or return "Expected '=' after 'fields' but found '$condition'";
306             $self->{fields} = $condition;
307             last;
308             };
309             /^order$/ && do {
310             $condition =~ s/^=//
311             or return "Expected '=' after 'order' but found '$condition'";
312             $self->{order} = $condition;
313             last;
314             };
315             /^page$/ && do { # The page number
316             $condition =~ /^=([1-9]\d*)$/
317             or return "Unrecognised page number: $condition";
318             $self->{page} = $1 - 1;
319             last;
320             };
321             /^pagesize$/ && do { # The page size
322             $condition =~ /^=(\d+)$/
323             or return "Unrecognised page size: $condition";
324             $self->{pagesize} = $1;
325             defined($self->{XMLServer}->{maxpagesize})
326             && $self->{XMLServer}->{maxpagesize} > 0
327             and ( ($1 > 0 && $1 <= $self->{XMLServer}->{maxpagesize})
328             or return "Invalid page size: Must be between 1 " .
329             "and $self->{XMLServer}->{maxpagesize}");
330             last;
331             };
332             /^format$/ && $self->{userformat} && do {
333             $condition =~ s/^=//
334             or return "Expected '=' after 'format' but found '$condition'";
335             my $root = $self->{XMLServer}->{doc}->documentElement;
336             my $p = &$sql_ns($root);
337             $self->{template} = $root->findnodes("/$p:spec/$p:template[@"
338             . "id='$condition']")->shift
339             or return "Invalid format. Must be one of "
340             . join(', ', map("'" . $_->value . "'",
341             $root->findnodes("/$p:spec/$p:template/@"."id")))
342             . ".";
343             $self->{template}->localname eq 'template'
344             && $self->{template}->namespaceURI eq $our_ns
345             or croak "Template is not ";
346             $self->{main_table} = $self->{template}->getAttribute('table')
347             or croak "The element has no `table' attribute";
348             $self->{ns} = $self->{template}->getAttribute('default-namespace');
349             $p = &$sql_ns($self->{template});
350             $self->{record} = $self->{template}->findnodes(".//$p:record/*[1]")
351             ->shift
352             or croak "The element contains no element";
353             last;
354             };
355             # Anything else we treat as a search criterion
356             push @{$self->{criteria}}, [$key, $condition];
357             }
358             }
359             return undef;
360             }
361              
362             sub do_criteria {
363             my $self = shift;
364              
365             my $prefix = $self->{ns};
366             $prefix = &$sql_ns($self->{record}, $self->{ns})
367             if defined $self->{ns} && $self->{ns} ne '*';
368             my $p = &$sql_ns($self->{record});
369             foreach(@{$self->{criteria}}) {
370             my $key = $_->[0];
371             # Fix up a default namespace
372             $key = add_prefix($key, $prefix) if $prefix;
373             # Find the field
374             my @nodelist = $self->{record}->findnodes($key);
375             my $node;
376             if(@nodelist eq 1 && $nodelist[0]->isa('XML::LibXML::Attr')) {
377             my $name = $nodelist[0]->nodeName;
378             my $owner = $nodelist[0]->getOwnerElement;
379             my $q = &$sql_ns($owner);
380             $node = $owner->findnodes("$q:field[@"."attribute='$name']")->shift
381             or return "Attribute '$key' isn't a field";
382             } else {
383             my @nodes = $self->{record}->findnodes
384             ($key . "//$p:field[not(@"."attribute)]")
385             or return "Unknown field: '$key'";
386             @nodes eq 1 or return "Expression '$key' selects more than one field";
387             $node = shift @nodes;
388             }
389             $_->[0] = new DBIx::XMLServer::Field($self->{XMLServer}, $node);
390             }
391             return undef;
392             }
393              
394             sub _prune {
395             my $element = shift;
396             if($element->getAttributeNS($our_ns, 'keepme')) {
397             foreach my $child ($element->childNodes) {
398             _prune($child) if $child->isa('XML::LibXML::Element');
399             }
400             } else {
401             $element->unbindNode
402             unless ($element->namespaceURI || '') eq $our_ns # Hack to avoid pruning
403             && $element->localname eq 'field' # attribute fields
404             && $element->getAttribute('attribute');
405             }
406             }
407              
408             sub build_output {
409             my $self = shift;
410             my $doc = shift;
411              
412             # Create the output structure
413             my $new_template = $self->{template}->cloneNode(1);
414             $doc->adoptNode($new_template);
415             $doc->setDocumentElement($new_template);
416             my $p = &$sql_ns($new_template);
417             my $record = $new_template->findnodes(".//$p:record")->shift
418             or croak "There is no element in the template";
419             $self->{newrecord} = $record->findnodes('*')->shift
420             or croak "The element has no child element";
421              
422             $self->{rowcount} = 'NONE'
423             unless $new_template->findnodes(".//$p:meta[@ type='rows']")->size();
424              
425             # Find the nodes to return
426             if(defined $self->{fields}) {
427             my $prefix = $self->{ns};
428             $prefix = &$sql_ns($self->{newrecord}, $self->{ns})
429             if defined $self->{ns} && $self->{ns} ne '*';
430             my ($r, $s) = get_xpath($self->{fields});
431             return "Unexpected text: '$s'" if $s;
432             $r = add_prefix($r, $prefix) if $prefix;
433             $self->{fields} = $r;
434             } else {
435             $self->{fields} = '.';
436             }
437             my @nodeset = $self->{newrecord}->findnodes
438             ("($self->{fields})/descendant-or-self::*");
439             @nodeset > 0 or return "No elements match expression $self->{fields}";
440              
441             # Mark the subtree containing them
442             $self->{newrecord}->setAttributeNS($our_ns, 'keepme', 1);
443             foreach my $node (@nodeset) {
444             until($node->isa('XML::LibXML::Element') &&
445             $node->getAttributeNS($our_ns, "keepme")) {
446             $node->setAttributeNS($our_ns, "keepme", 1)
447             if $node->isa('XML::LibXML::Element');
448             $node = $node->parentNode;
449             }
450             }
451              
452             # Find the nodes to order by
453             if(defined $self->{order}) {
454             my $prefix = $self->{ns};
455             $prefix = &$sql_ns($self->{newrecord}, $self->{ns})
456             if defined $self->{ns} && $self->{ns} ne '*';
457             my $order = $self->{order};
458             my @order;
459             while ( $order ne '' ) {
460             my ($xpath, $more) = get_xpath($order);
461             $xpath = add_prefix($xpath, $prefix) if $prefix;
462             $xpath =~ s/ +(ascending|descending) *$//;
463             my $dir = $1;
464             my @o = $self->{newrecord}->findnodes($xpath)
465             or return "Invalid field in order clause: $xpath\n";
466             foreach (@o) {
467             my @f = $_->findnodes(
468             $_->nodeType == XML::LibXML::XML_ATTRIBUTE_NODE
469             ? "../$p:field[\@attribute='".$_->nodeName."']"
470             : ".//$p:field" )
471             or return "No non-static data matched by order clause: $xpath\n";
472             foreach (@f) {
473             push @order,
474             new DBIx::XMLServer::OrderSpec($self->{XMLServer}, $_, $dir);
475             }
476             }
477             return "Unexpected order: '$order'"
478             unless $more eq '' || $more =~ s/^,//;
479             $order = $more;
480             }
481             $self->{order} = \@order;
482             }
483              
484             # Prune away what we don't want to return
485             _prune($self->{newrecord});
486              
487             return undef;
488             }
489              
490             sub build_fields {
491             my $self = shift;
492             my @fields;
493             my $p = &$sql_ns($self->{newrecord});
494             foreach($self->{newrecord}->findnodes(".//$p:field")) {
495             push @fields, new DBIx::XMLServer::Field($self->{XMLServer}, $_);
496             }
497             $self->{fields} = \@fields;
498             return undef;
499             }
500              
501             sub add_join {
502             my ($self, $table) = @_;
503             return unless $table;
504             do {
505             my $root = $self->{XMLServer}->{doc}->documentElement;
506             my $p = &$sql_ns($root);
507             my $tabledef = $root->find("/$p:spec/$p:table[@"."name='$table']")->shift
508             or croak "Unknown table reference: $table";
509             my $jointo = $tabledef->getAttribute('jointo');
510             my $join = '';
511             do {
512             $self->add_join($jointo);
513             $join = uc $tabledef->getAttribute('join') || '';
514             $join .= ' JOIN ';
515             } if $jointo;
516             my $sqlname = $tabledef->getAttribute('sqlname')
517             or croak "Table `$table' has no `sqlname' attribute";
518             $join .= "$sqlname AS $table";
519             do {
520             if(my $using = $tabledef->getAttribute('using')) {
521             $join .= " ON $jointo.$using = $table.$using";
522             } elsif(my $ref = $tabledef->getAttribute('refcolumn')) {
523             my $key = $tabledef->getAttribute('keycolumn')
524             or croak "Table $table has `refcolumn' without `keycolumn'";
525             $join .= " ON $jointo.$ref = $table.$key";
526             } elsif(my $on = $tabledef->getAttribute('on')) {
527             $join .= " ON $on";
528             }
529             } if $jointo;
530             push @{$self->{jointext}}, $join;
531             $self->{joinhash}->{$table} = 1;
532             } unless $self->{joinhash}->{$table};
533             }
534              
535             sub parse {
536             my ($self, $arg) = @_;
537             my $err;
538              
539             $self->{doc} = new XML::LibXML::Document;
540             $self->{arg} = $arg;
541             $err = $self->real_parse($arg) and return $err;
542             $err = $self->do_criteria and return $err;
543             $err = $self->build_output($self->{doc}) and return $err;
544             $err = $self->build_fields and return $err;
545              
546             $self->{jointext} = [];
547             $self->{joinhash} = {};
548             $self->add_join($self->{main_table});
549             foreach my $x (@{$self->{criteria}}) {
550             foreach($x->[0]->join) {
551             $self->add_join($_);
552             }
553             }
554              
555             my $select;
556             my $from;
557             my $where;
558             my $order;
559             my $limit;
560              
561             eval {
562             $where = join(' AND ', map($_->[0]->where($_->[1]),
563             @{$self->{criteria}})) || '1';
564             $from = join(' ', @{$self->{jointext}});
565             };
566             return $@ if $@;
567              
568             $self->{count_query} = "SELECT COUNT(*) FROM $from WHERE $where";
569              
570             foreach my $f (@{$self->{fields}}) {
571             foreach ($f->join) {
572             $self->add_join($_);
573             }
574             }
575              
576             foreach my $o (@{$self->{order}}) {
577             foreach ($o->{field}->join) {
578             $self->add_join($_);
579             }
580             }
581              
582             eval {
583             $select = join(',', map($_->select, @{$self->{fields}})) || '0';
584             $order = (defined $self->{order} && scalar @{$self->{order}}) ?
585             ' ORDER BY ' . join(',', map($_->orderspec, @{$self->{order}}))
586             : '';
587             $limit = ($self->{pagesize} > 0) ?
588             ' LIMIT ' . ($self->{page} * $self->{pagesize}) . ", $self->{pagesize}"
589             : '';
590             $from = join(' ', @{$self->{jointext}});
591             };
592             return $@ if $@;
593              
594             $self->{query} = "SELECT $select FROM $from WHERE $where$order$limit";
595              
596             return undef;
597             }
598              
599             # Process a request
600             # $results = $xmlout->process();
601             sub process {
602             my $self = shift;
603             my %args = @_;
604             my $err;
605              
606             $self->{query}
607             or croak "DBIx::XMLServer::Request: must call parse before process";
608              
609             $args{rowcount} = $self->{rowcount} unless $args{rowcount};
610              
611             # Do the query
612             my $query = $self->{query};
613             $query =~ s/^SELECT/SELECT SQL_CALC_FOUND_ROWS/
614             if $args{rowcount} eq 'FOUND_ROWS';
615             my $sth = $self->{XMLServer}->{dbh}->prepare($query);
616             $sth->execute or croak $sth->errstr;
617              
618             # Put the data into the result tree
619             my $r = $self->{newrecord}->parentNode;
620             my @row;
621             while(@row = $sth->fetchrow_array) {
622              
623             # Clone the template record and insert after the previous record
624             $r = $r->parentNode->insertAfter($self->{newrecord}->cloneNode(1), $r);
625              
626             # Fill in the values
627             my $p = &$sql_ns($self->{newrecord});
628             my @n = $r->findnodes(".//$p:field");
629             foreach(@{$self->{fields}}) {
630             eval { $_->result(shift @n, \@row); };
631             return $@ if $@;
632             }
633              
634             }
635              
636             my $rows = 0;
637             do {
638             my @r;
639             @r = $self->{XMLServer}->{dbh}->selectrow_array('SELECT FOUND_ROWS()')
640             or croak $self->{XMLServer}->{dbh}->errstr;
641             $rows = $r[0];
642             } if $args{rowcount} eq 'FOUND_ROWS';
643             do {
644             my @r;
645             @r = $self->{XMLServer}->{dbh}->selectrow_array($self->{count_query})
646             or croak $self->{XMLServer}->{dbh}->errstr;
647             $rows = $r[0];
648             } if $args{rowcount} eq 'COUNT';
649              
650             my %params = (
651             'args' => $self->{arg},
652             'page' => $self->{page},
653             'pagesize' => $self->{pagesize},
654             'query' => $self->{query},
655             'rows' => $rows,
656             );
657              
658             # Process through XSLT to produce the result
659             return $self->{XMLServer}->{xslt}->transform($self->{doc},
660             XML::LibXSLT::xpath_to_string(%params));
661             }
662              
663             1;
664              
665             package DBIx::XMLServer;
666             use Carp;
667              
668             sub add_type {
669             my $self = shift;
670             my $type = shift;
671             my $name = $type->getAttribute('name')
672             or croak("Field type found with no name");
673            
674             my $p = &$sql_ns($type);
675             my $package_name = $type->findnodes("$p:module");
676             if($package_name->size) {
677             $package_name = "$package_name";
678             eval "use $package_name;";
679             croak "Error loading module `$package_name' for field type"
680             . " definition `$name':\n$@" if $@;
681             } else {
682             $package_name = "DBIx::XMLServer::Types::$name";
683             my $where = $type->findnodes("$p:where");
684             $where = $where->size ? "sub where { $where }" : '';
685             my $select = $type->findnodes("$p:select");
686             $select = $select->size ? "sub select { $select }" : '';
687             my $join = $type->findnodes("$p:join");
688             $join = $join->size ? "sub join { $join }" : '';
689             my $value = $type->findnodes("$p:value");
690             $value = $value->size ? "sub value { $value }" : '';
691             my $init = $type->findnodes("$p:init");
692             $init = $init->size ? "sub init { $init }" : '';
693             my $isa = $type->findnodes("$p:isa");
694             $isa = $isa->size ? "$isa" : 'DBIx::XMLServer::Field';
695             $isa =~ s/\s+//g;
696             eval <
697             package $package_name;
698             use XML::LibXML;
699             our \@ISA = ('$isa');
700             $init
701             $select
702             $where
703             $join
704             $value
705             1;
706             EOF
707             croak "Error compiling field type definition `$name':\n$@" if $@;
708             }
709             $self->{types}->{$name} = $package_name;
710             }
711              
712             # Object constructor
713             # $xmlout = new DBIx::XMLServer($dbh, $doc[, $template]);
714             sub new {
715             my $proto = shift;
716             my $class = ref($proto) || $proto;
717             my $self;
718             my $doc;
719              
720             # Deal with the parameters
721             if(ref $_[0]) { # dbh, doc [, template]
722             $self = {};
723             $self->{dbh} = shift or croak "No database handle supplied";
724             $doc = shift or croak "No template file supplied";
725             $self->{template} = shift;
726             } else { # Named parameters
727             $self = { @_ };
728             $self->{dbh} or croak "No database handle supplied";
729             $doc = $self->{doc} or croak "No template file supplied";
730             }
731             bless($self, $class);
732              
733             my $parser = new XML::LibXML;
734             ref $doc or $doc = $parser->parse_file($doc)
735             or croak "Couldn't parse template file '$doc'";
736             $doc->isa('XML::LibXML::Document')
737             or croak "This isn't a XML::LibXML::Document";
738             $self->{doc} = $doc;
739              
740             my $root = $doc->documentElement;
741             $root->localname eq 'spec' && $root->namespaceURI eq $our_ns
742             or croak "Document element is not ";
743              
744             my $p = &$sql_ns($root);
745              
746             # Find all the field type definitions and parse them
747             $self->{types} = {};
748             foreach($doc->findnodes("/$p:spec/$p:type")) {
749             $self->add_type($_);
750             }
751              
752             # Find the template
753             $self->{template}
754             or $self->{template} = $doc->find("/$p:spec/$p:template")
755             ->shift
756             or croak "No element found";
757              
758             $self->{template}->isa('XML::LibXML::Element')
759             or croak "Template is not a XML::LibXML::Element";
760              
761             $self->{template}->localname eq 'template'
762             && $self->{template}->namespaceURI eq $our_ns
763             or croak "Template is not ";
764              
765             # Parse our XSLT stylesheet
766             my $xslt = new XML::LibXSLT;
767             my $f = $INC{'DBIx/XMLServer.pm'};
768             $f =~ s/XMLServer\.pm/XMLServer\/xmlout\.xsl/;
769             my $style_doc = $parser->parse_file($f)
770             or croak "Couldn't open stylesheet '$f'";
771             $self->{xslt} = $xslt->parse_stylesheet($style_doc)
772             or croak "Error parsing stylesheet '$f'";
773              
774             $self->{maxpagesize} = 0 unless $self->{maxpagesize};
775             $self->{rowcount} = 'NONE' unless defined $self->{rowcount};
776              
777             return $self;
778             }
779              
780             sub process {
781             my $self = shift;
782             my %args;
783             my $err;
784              
785             # Process arguments
786             if($#_ <= 1 && $_[0] ne 'query') {
787             $args{query} = shift
788             or croak "No query string given";
789             } else { # Named parameters
790             %args = @_;
791             }
792              
793             $args{XMLServer} = $self;
794             my $request = new DBIx::XMLServer::Request(%args);
795             $err = $request->parse($args{query}) and return $err;
796             return $request->process();
797             }
798              
799             1;
800             __END__