File Coverage

blib/lib/XML/NamespaceSupport.pm
Criterion Covered Total %
statement 128 156 82.0
branch 41 62 66.1
condition 14 27 51.8
subroutine 25 28 89.2
pod 14 15 93.3
total 222 288 77.0


line stmt bran cond sub pod time code
1              
2             ###
3             # XML::NamespaceSupport - a simple generic namespace processor
4             # Robin Berjon
5             ###
6              
7             package XML::NamespaceSupport;
8 1     1   31065 use strict;
  1         3  
  1         686  
9 1     1   11 use constant FATALS => 0; # root object
  1         3  
  1         211  
10 1     1   10 use constant NSMAP => 1;
  1         12  
  1         88  
11 1     1   9 use constant UNKNOWN_PREF => 2;
  1         3  
  1         3184  
12 1     1   11 use constant AUTO_PREFIX => 3;
  1         2  
  1         350  
13 1     1   7 use constant XMLNS_11 => 4;
  1         2  
  1         64  
14 1     1   6 use constant DEFAULT => 0; # maps
  1         2  
  1         198  
15 1     1   49 use constant PREFIX_MAP => 1;
  1         2  
  1         56  
16 1     1   6 use constant DECLARATIONS => 2;
  1         2  
  1         60  
17              
18 1     1   7 use vars qw($VERSION $NS_XMLNS $NS_XML);
  1         2  
  1         1247  
19             $VERSION = '1.11';
20             $NS_XMLNS = 'http://www.w3.org/2000/xmlns/';
21             $NS_XML = 'http://www.w3.org/XML/1998/namespace';
22              
23              
24             # add the ns stuff that baud wants based on Java's xml-writer
25              
26              
27             #-------------------------------------------------------------------#
28             # constructor
29             #-------------------------------------------------------------------#
30             sub new {
31 1 50   1 1 15 my $class = ref($_[0]) ? ref(shift) : shift;
32 1         3 my $options = shift;
33 1         5 my $self = [
34             1, # FATALS
35             [[ # NSMAP
36             undef, # DEFAULT
37             { xml => $NS_XML }, # PREFIX_MAP
38             undef, # DECLARATIONS
39             ]],
40             'aaa', # UNKNOWN_PREF
41             0, # AUTO_PREFIX
42             1, # XML_11
43             ];
44 1 50       6 $self->[NSMAP]->[0]->[PREFIX_MAP]->{xmlns} = $NS_XMLNS if $options->{xmlns};
45 1 50       7 $self->[FATALS] = $options->{fatal_errors} if defined $options->{fatal_errors};
46 1 50       3 $self->[AUTO_PREFIX] = $options->{auto_prefix} if defined $options->{auto_prefix};
47 1 50       4 $self->[XMLNS_11] = $options->{xmlns_11} if defined $options->{xmlns_11};
48 1         5 return bless $self, $class;
49             }
50             #-------------------------------------------------------------------#
51              
52             #-------------------------------------------------------------------#
53             # reset() - return to the original state (for reuse)
54             #-------------------------------------------------------------------#
55             sub reset {
56 1     1 1 2 my $self = shift;
57 1         1 $#{$self->[NSMAP]} = 0;
  1         5  
58             }
59             #-------------------------------------------------------------------#
60              
61             #-------------------------------------------------------------------#
62             # push_context() - add a new empty context to the stack
63             #-------------------------------------------------------------------#
64             sub push_context {
65 5     5 1 10 my $self = shift;
66 5         13 push @{$self->[NSMAP]}, [
  5         33  
67             $self->[NSMAP]->[-1]->[DEFAULT],
68 5         5 { %{$self->[NSMAP]->[-1]->[PREFIX_MAP]} },
69             [],
70             ];
71             }
72             #-------------------------------------------------------------------#
73              
74             #-------------------------------------------------------------------#
75             # pop_context() - remove the topmost context fromt the stack
76             #-------------------------------------------------------------------#
77             sub pop_context {
78 3     3 1 5 my $self = shift;
79 3 50       4 die 'Trying to pop context without push context' unless @{$self->[NSMAP]} > 1;
  3         10  
80 3         4 pop @{$self->[NSMAP]};
  3         8  
81             }
82             #-------------------------------------------------------------------#
83              
84             #-------------------------------------------------------------------#
85             # declare_prefix() - declare a prefix in the current scope
86             #-------------------------------------------------------------------#
87             sub declare_prefix {
88 12     12 1 218 my $self = shift;
89 12         15 my $prefix = shift;
90 12         15 my $value = shift;
91              
92 12 50 66     34 warn <<' EOWARN' unless defined $prefix or $self->[AUTO_PREFIX];
93             Prefix was undefined.
94             If you wish to set the default namespace, use the empty string ''.
95             If you wish to autogenerate prefixes, set the auto_prefix option
96             to a true value.
97             EOWARN
98              
99 1     1   7 no warnings 'uninitialized';
  1         2  
  1         375  
100 12 50 33     77 if ($prefix eq 'xml' and $value ne $NS_XML) {
    50 33        
    50 33        
101 0         0 die "The xml prefix can only be bound to the $NS_XML namespace."
102             }
103             elsif ($value eq $NS_XML and $prefix ne 'xml') {
104 0         0 die "the $NS_XML namespace can only be bound to the xml prefix.";
105             }
106             elsif ($value eq $NS_XML and $prefix eq 'xml') {
107 0         0 return 1;
108             }
109 12 100       40 return 0 if index(lc($prefix), 'xml') == 0;
110 1     1   12 use warnings 'uninitialized';
  1         1  
  1         2599  
111              
112 11 100 100     43 if (defined $prefix and $prefix eq '') {
113 4         8 $self->[NSMAP]->[-1]->[DEFAULT] = $value;
114             }
115             else {
116 7 100 100     30 die "Cannot undeclare prefix $prefix" if $value eq '' and not $self->[XMLNS_11];
117 6 100 66     26 if (not defined $prefix and $self->[AUTO_PREFIX]) {
    50 33        
118 1         2 while (1) {
119 1         3 $prefix = $self->[UNKNOWN_PREF]++;
120 1 50       6 last if not exists $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix};
121             }
122             }
123             elsif (not defined $prefix and not $self->[AUTO_PREFIX]) {
124 0         0 return 0;
125             }
126 6         16 $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix} = $value;
127             }
128 10         11 push @{$self->[NSMAP]->[-1]->[DECLARATIONS]}, $prefix;
  10         22  
129 10         36 return 1;
130             }
131             #-------------------------------------------------------------------#
132              
133             #-------------------------------------------------------------------#
134             # declare_prefixes() - declare several prefixes in the current scope
135             #-------------------------------------------------------------------#
136             sub declare_prefixes {
137 0     0 1 0 my $self = shift;
138 0         0 my %prefixes = @_;
139 0         0 while (my ($k,$v) = each %prefixes) {
140 0         0 $self->declare_prefix($k,$v);
141             }
142             }
143             #-------------------------------------------------------------------#
144              
145             #-------------------------------------------------------------------#
146             # undeclare_prefix
147             #-------------------------------------------------------------------#
148             sub undeclare_prefix {
149 0     0 1 0 my $self = shift;
150 0         0 my $prefix = shift;
151 0 0 0     0 return unless not defined $prefix or $prefix eq '';
152 0 0       0 return unless exists $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix};
153              
154 0         0 my ( $tfix ) = grep { $_ eq $prefix } @{$self->[NSMAP]->[-1]->[DECLARATIONS]};
  0         0  
  0         0  
155 0 0       0 if ( not defined $tfix ) {
156 0         0 die "prefix $prefix not declared in this context\n";
157             }
158              
159 0         0 @{$self->[NSMAP]->[-1]->[DECLARATIONS]} = grep { $_ ne $prefix } @{$self->[NSMAP]->[-1]->[DECLARATIONS]};
  0         0  
  0         0  
  0         0  
160 0         0 delete $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix};
161             }
162             #-------------------------------------------------------------------#
163              
164             #-------------------------------------------------------------------#
165             # get_prefix() - get a (random) prefix for a given URI
166             #-------------------------------------------------------------------#
167             sub get_prefix {
168 2     2 1 5 my $self = shift;
169 2         4 my $uri = shift;
170              
171             # we have to iterate over the whole hash here because if we don't
172             # the iterator isn't reset and the next pass will fail
173 2         4 my $pref;
174 2         5 while (my ($k, $v) = each %{$self->[NSMAP]->[-1]->[PREFIX_MAP]}) {
  9         41  
175 7 100       20 $pref = $k if $v eq $uri;
176             }
177 2         11 return $pref;
178             }
179             #-------------------------------------------------------------------#
180              
181             #-------------------------------------------------------------------#
182             # get_prefixes() - get all the prefixes for a given URI
183             #-------------------------------------------------------------------#
184             sub get_prefixes {
185 8     8 1 785 my $self = shift;
186 8         17 my $uri = shift;
187              
188 8 100       175 return keys %{$self->[NSMAP]->[-1]->[PREFIX_MAP]} unless defined $uri;
  5         199  
189 3         4 return grep { $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$_} eq $uri } keys %{$self->[NSMAP]->[-1]->[PREFIX_MAP]};
  13         49  
  3         12  
190             }
191             #-------------------------------------------------------------------#
192              
193             #-------------------------------------------------------------------#
194             # get_declared_prefixes() - get all prefixes declared in the last context
195             #-------------------------------------------------------------------#
196             sub get_declared_prefixes {
197 3     3 1 6 return @{$_[0]->[NSMAP]->[-1]->[DECLARATIONS]};
  3         20  
198             }
199             #-------------------------------------------------------------------#
200              
201             #-------------------------------------------------------------------#
202             # get_uri() - get an URI given a prefix
203             #-------------------------------------------------------------------#
204             sub get_uri {
205 3     3 1 5 my $self = shift;
206 3         4 my $prefix = shift;
207              
208 3 50       8 warn "Prefix must not be undef in get_uri(). The emtpy prefix must be ''" unless defined $prefix;
209              
210 3 100       10 return $self->[NSMAP]->[-1]->[DEFAULT] if $prefix eq '';
211 2 50       15 return $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix} if exists $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix};
212 0         0 return undef;
213             }
214             #-------------------------------------------------------------------#
215              
216             #-------------------------------------------------------------------#
217             # process_name() - provide details on a name
218             #-------------------------------------------------------------------#
219             sub process_name {
220 2     2 1 4 my $self = shift;
221 2         4 my $qname = shift;
222 2         2 my $aflag = shift;
223              
224 2 50       6 if ($self->[FATALS]) {
225 0         0 return( ($self->_get_ns_details($qname, $aflag))[0,2], $qname );
226             }
227             else {
228 2         17 eval { return( ($self->_get_ns_details($qname, $aflag))[0,2], $qname ); }
  2         7  
229             }
230             }
231             #-------------------------------------------------------------------#
232              
233             #-------------------------------------------------------------------#
234             # process_element_name() - provide details on a element's name
235             #-------------------------------------------------------------------#
236             sub process_element_name {
237 9     9 1 698 my $self = shift;
238 9         17 my $qname = shift;
239              
240 9 100       18 if ($self->[FATALS]) {
241 4         8 return $self->_get_ns_details($qname, 0);
242             }
243             else {
244 5         7 eval { return $self->_get_ns_details($qname, 0); }
  5         11  
245             }
246             }
247             #-------------------------------------------------------------------#
248              
249              
250             #-------------------------------------------------------------------#
251             # process_attribute_name() - provide details on a attribute's name
252             #-------------------------------------------------------------------#
253             sub process_attribute_name {
254 8     8 1 767 my $self = shift;
255 8         11 my $qname = shift;
256              
257 8 100       19 if ($self->[FATALS]) {
258 4         9 return $self->_get_ns_details($qname, 1);
259             }
260             else {
261 4         5 eval { return $self->_get_ns_details($qname, 1); }
  4         8  
262             }
263             }
264             #-------------------------------------------------------------------#
265              
266              
267             #-------------------------------------------------------------------#
268             # ($ns, $prefix, $lname) = $self->_get_ns_details($qname, $f_attr)
269             # returns ns, prefix, and lname for a given attribute name
270             # >> the $f_attr flag, if set to one, will work for an attribute
271             #-------------------------------------------------------------------#
272             sub _get_ns_details {
273 19     19   25 my $self = shift;
274 19         45 my $qname = shift;
275 19         20 my $aflag = shift;
276              
277 19         19 my ($ns, $prefix, $lname);
278 19 100       97 (my ($tmp_prefix, $tmp_lname) = split /:/, $qname, 3)
279             < 3 or die "Invalid QName: $qname";
280              
281             # no prefix
282 18         30 my $cur_map = $self->[NSMAP]->[-1];
283 18 100       33 if (not defined($tmp_lname)) {
284 5         7 $prefix = undef;
285 5         6 $lname = $qname;
286             # attr don't have a default namespace
287 5 100       19 $ns = ($aflag) ? undef : $cur_map->[DEFAULT];
288             }
289              
290             # prefix
291             else {
292 13 100       32 if (exists $cur_map->[PREFIX_MAP]->{$tmp_prefix}) {
293 9         10 $prefix = $tmp_prefix;
294 9         11 $lname = $tmp_lname;
295 9         17 $ns = $cur_map->[PREFIX_MAP]->{$prefix}
296             }
297             else { # no ns -> lname == name, all rest undef
298 4         37 die "Undeclared prefix: $tmp_prefix";
299             }
300             }
301              
302 14         78 return ($ns, $prefix, $lname);
303             }
304             #-------------------------------------------------------------------#
305              
306             #-------------------------------------------------------------------#
307             # parse_jclark_notation() - parse the Clarkian notation
308             #-------------------------------------------------------------------#
309             sub parse_jclark_notation {
310 0     0 0   shift;
311 0           my $jc = shift;
312 0           $jc =~ m/^\{(.*)\}([^}]+)$/;
313 0           return $1, $2;
314             }
315             #-------------------------------------------------------------------#
316              
317              
318             #-------------------------------------------------------------------#
319             # Java names mapping
320             #-------------------------------------------------------------------#
321             *XML::NamespaceSupport::pushContext = \&push_context;
322             *XML::NamespaceSupport::popContext = \&pop_context;
323             *XML::NamespaceSupport::declarePrefix = \&declare_prefix;
324             *XML::NamespaceSupport::declarePrefixes = \&declare_prefixes;
325             *XML::NamespaceSupport::getPrefix = \&get_prefix;
326             *XML::NamespaceSupport::getPrefixes = \&get_prefixes;
327             *XML::NamespaceSupport::getDeclaredPrefixes = \&get_declared_prefixes;
328             *XML::NamespaceSupport::getURI = \&get_uri;
329             *XML::NamespaceSupport::processName = \&process_name;
330             *XML::NamespaceSupport::processElementName = \&process_element_name;
331             *XML::NamespaceSupport::processAttributeName = \&process_attribute_name;
332             *XML::NamespaceSupport::parseJClarkNotation = \&parse_jclark_notation;
333             *XML::NamespaceSupport::undeclarePrefix = \&undeclare_prefix;
334             #-------------------------------------------------------------------#
335              
336              
337             1;
338             #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
339             #`,`, Documentation `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
340             #```````````````````````````````````````````````````````````````````#
341              
342             =pod
343              
344             =head1 NAME
345              
346             XML::NamespaceSupport - a simple generic namespace support class
347              
348             =head1 SYNOPSIS
349              
350             use XML::NamespaceSupport;
351             my $nsup = XML::NamespaceSupport->new;
352              
353             # add a new empty context
354             $nsup->push_context;
355             # declare a few prefixes
356             $nsup->declare_prefix($prefix1, $uri1);
357             $nsup->declare_prefix($prefix2, $uri2);
358             # the same shorter
359             $nsup->declare_prefixes($prefix1 => $uri1, $prefix2 => $uri2);
360              
361             # get a single prefix for a URI (randomly)
362             $prefix = $nsup->get_prefix($uri);
363             # get all prefixes for a URI (probably better)
364             @prefixes = $nsup->get_prefixes($uri);
365             # get all prefixes in scope
366             @prefixes = $nsup->get_prefixes();
367             # get all prefixes that were declared for the current scope
368             @prefixes = $nsup->get_declared_prefixes;
369             # get a URI for a given prefix
370             $uri = $nsup->get_uri($prefix);
371              
372             # get info on a qname (java-ish way, it's a bit weird)
373             ($ns_uri, $local_name, $qname) = $nsup->process_name($qname, $is_attr);
374             # the same, more perlish
375             ($ns_uri, $prefix, $local_name) = $nsup->process_element_name($qname);
376             ($ns_uri, $prefix, $local_name) = $nsup->process_attribute_name($qname);
377              
378             # remove the current context
379             $nsup->pop_context;
380              
381             # reset the object for reuse in another document
382             $nsup->reset;
383              
384             # a simple helper to process Clarkian Notation
385             my ($ns, $lname) = $nsup->parse_jclark_notation('{http://foo}bar');
386             # or (given that it doesn't care about the object
387             my ($ns, $lname) = XML::NamespaceSupport->parse_jclark_notation('{http://foo}bar');
388              
389              
390             =head1 DESCRIPTION
391              
392             This module offers a simple to process namespaced XML names (unames)
393             from within any application that may need them. It also helps maintain
394             a prefix to namespace URI map, and provides a number of basic checks.
395              
396             The model for this module is SAX2's NamespaceSupport class, readable at
397             http://www.megginson.com/SAX/Java/javadoc/org/xml/sax/helpers/NamespaceSupport.html.
398             It adds a few perlisations where we thought it appropriate.
399              
400             =head1 METHODS
401              
402             =over 4
403              
404             =item * XML::NamespaceSupport->new(\%options)
405              
406             A simple constructor.
407              
408             The options are C, C, and C
409              
410             If C is turned on (it is off by default) the mapping from the
411             xmlns prefix to the URI defined for it in DOM level 2 is added to the
412             list of predefined mappings (which normally only contains the xml
413             prefix mapping).
414              
415             If C is turned off (it is on by default) a number of
416             validity errors will simply be flagged as failures, instead of
417             die()ing.
418              
419             If C is turned on (it is off by default) when one
420             provides a prefix of C to C it will generate a
421             random prefix mapped to that namespace. Otherwise an undef prefix will
422             trigger a warning (you should probably know what you're doing if you
423             turn this option on).
424              
425             If C us turned off, it becomes illegal to undeclare namespace
426             prefixes. It is on by default. This behaviour is compliant with Namespaces
427             in XML 1.1, turning it off reverts you to version 1.0.
428              
429             =item * $nsup->push_context
430              
431             Adds a new empty context to the stack. You can then populate it with
432             new prefixes defined at this level.
433              
434             =item * $nsup->pop_context
435              
436             Removes the topmost context in the stack and reverts to the previous
437             one. It will die() if you try to pop more than you have pushed.
438              
439             =item * $nsup->declare_prefix($prefix, $uri)
440              
441             Declares a mapping of $prefix to $uri, at the current level.
442              
443             Note that with C turned on, if you declare a prefix
444             mapping in which $prefix is undef(), you will get an automatic prefix
445             selected for you. If it is off you will get a warning.
446              
447             This is useful when you deal with code that hasn't kept prefixes around
448             and need to reserialize the nodes. It also means that if you want to
449             set the default namespace (ie with an empty prefix) you must use the
450             empty string instead of undef. This behaviour is consistent with the
451             SAX 2.0 specification.
452              
453             =item * $nsup->declare_prefixes(%prefixes2uris)
454              
455             Declares a mapping of several prefixes to URIs, at the current level.
456              
457             =item * $nsup->get_prefix($uri)
458              
459             Returns a prefix given an URI. Note that as several prefixes may be
460             mapped to the same URI, it returns an arbitrary one. It'll return
461             undef on failure.
462              
463             =item * $nsup->get_prefixes($uri)
464              
465             Returns an array of prefixes given an URI. It'll return all the
466             prefixes if the uri is undef.
467              
468             =item * $nsup->get_declared_prefixes
469              
470             Returns an array of all the prefixes that have been declared within
471             this context, ie those that were declared on the last element, not
472             those that were declared above and are simply in scope.
473              
474             =item * $nsup->get_uri($prefix)
475              
476             Returns a URI for a given prefix. Returns undef on failure.
477              
478             =item * $nsup->process_name($qname, $is_attr)
479              
480             Given a qualified name and a boolean indicating whether this is an
481             attribute or another type of name (those are differently affected by
482             default namespaces), it returns a namespace URI, local name, qualified
483             name tuple. I know that that is a rather abnormal list to return, but
484             it is so for compatibility with the Java spec. See below for more
485             Perlish alternatives.
486              
487             If the prefix is not declared, or if the name is not valid, it'll
488             either die or return undef depending on the current setting of
489             C.
490              
491             =item * $nsup->undeclare_prefix($prefix);
492              
493             Removes a namespace prefix from the current context. This function may
494             be used in SAX's end_prefix_mapping when there is fear that a namespace
495             declaration might be available outside their scope (which shouldn't
496             normally happen, but you never know ;). This may be needed in order to
497             properly support Namespace 1.1.
498              
499             =item * $nsup->process_element_name($qname)
500              
501             Given a qualified name, it returns a namespace URI, prefix, and local
502             name tuple. This method applies to element names.
503              
504             If the prefix is not declared, or if the name is not valid, it'll
505             either die or return undef depending on the current setting of
506             C.
507              
508             =item * $nsup->process_attribute_name($qname)
509              
510             Given a qualified name, it returns a namespace URI, prefix, and local
511             name tuple. This method applies to attribute names.
512              
513             If the prefix is not declared, or if the name is not valid, it'll
514             either die or return undef depending on the current setting of
515             C.
516              
517             =item * $nsup->reset
518              
519             Resets the object so that it can be reused on another document.
520              
521             =back
522              
523             All methods of the interface have an alias that is the name used in
524             the original Java specification. You can use either name
525             interchangeably. Here is the mapping:
526              
527             Java name Perl name
528             ---------------------------------------------------
529             pushContext push_context
530             popContext pop_context
531             declarePrefix declare_prefix
532             declarePrefixes declare_prefixes
533             getPrefix get_prefix
534             getPrefixes get_prefixes
535             getDeclaredPrefixes get_declared_prefixes
536             getURI get_uri
537             processName process_name
538             processElementName process_element_name
539             processAttributeName process_attribute_name
540             parseJClarkNotation parse_jclark_notation
541             undeclarePrefix undeclare_prefix
542              
543             =head1 VARIABLES
544              
545             Two global variables are made available to you. They used to be constants but
546             simple scalars are easier to use in a number of contexts. They are not
547             exported but can easily be accessed from any package, or copied into it.
548              
549             =over 4
550              
551             =item * C<$NS_XMLNS>
552              
553             The namespace for xmlns prefixes, http://www.w3.org/2000/xmlns/.
554              
555             =item * C<$NS_XML>
556              
557             The namespace for xml prefixes, http://www.w3.org/XML/1998/namespace.
558              
559             =back
560              
561             =head1 TODO
562              
563             - add more tests
564             - optimise here and there
565              
566             =head1 AUTHOR
567              
568             Robin Berjon, robin@knowscape.com, with lots of it having been done
569             by Duncan Cameron, and a number of suggestions from the perl-xml
570             list.
571              
572             =head1 COPYRIGHT
573              
574             Copyright (c) 2001-2005 Robin Berjon. All rights reserved. This program is
575             free software; you can redistribute it and/or modify it under the same terms
576             as Perl itself.
577              
578             =head1 SEE ALSO
579              
580             XML::Parser::PerlSAX
581              
582             =cut
583