File Coverage

blib/lib/XML/NamespaceSupport.pm
Criterion Covered Total %
statement 152 158 96.2
branch 48 64 75.0
condition 17 27 62.9
subroutine 28 28 100.0
pod 14 15 93.3
total 259 292 88.7


line stmt bran cond sub pod time code
1             package XML::NamespaceSupport;
2 1     1   16315 use strict;
  1         2  
  1         41  
3              
4             our $VERSION = '1.12_9'; # TRIAL VERSION
5              
6             # ABSTRACT: A simple generic namespace processor
7              
8 1     1   5 use constant FATALS => 0; # root object
  1         1  
  1         66  
9 1     1   6 use constant NSMAP => 1;
  1         5  
  1         46  
10 1     1   5 use constant UNKNOWN_PREF => 2;
  1         2  
  1         40  
11 1     1   5 use constant AUTO_PREFIX => 3;
  1         2  
  1         32  
12 1     1   5 use constant XMLNS_11 => 4;
  1         2  
  1         37  
13 1     1   5 use constant DEFAULT => 0; # maps
  1         1  
  1         33  
14 1     1   5 use constant PREFIX_MAP => 1;
  1         2  
  1         34  
15 1     1   5 use constant DECLARATIONS => 2;
  1         2  
  1         35  
16              
17 1     1   6 use vars qw($NS_XMLNS $NS_XML);
  1         2  
  1         309  
18             $NS_XMLNS = 'http://www.w3.org/2000/xmlns/';
19             $NS_XML = 'http://www.w3.org/XML/1998/namespace';
20              
21              
22             # add the ns stuff that baud wants based on Java's xml-writer
23              
24             #-------------------------------------------------------------------#
25             # constructor
26             #-------------------------------------------------------------------#
27             sub new {
28 5 50   5 1 1083 my $class = ref($_[0]) ? ref(shift) : shift;
29 5         9 my $options = shift;
30 5         19 my $self = [
31             1, # FATALS
32             [[ # NSMAP
33             undef, # DEFAULT
34             { xml => $NS_XML }, # PREFIX_MAP
35             undef, # DECLARATIONS
36             ]],
37             'aaa', # UNKNOWN_PREF
38             0, # AUTO_PREFIX
39             1, # XML_11
40             ];
41 5 50       25 $self->[NSMAP]->[0]->[PREFIX_MAP]->{xmlns} = $NS_XMLNS if $options->{xmlns};
42 5 50       15 $self->[FATALS] = $options->{fatal_errors} if defined $options->{fatal_errors};
43 5 50       14 $self->[AUTO_PREFIX] = $options->{auto_prefix} if defined $options->{auto_prefix};
44 5 50       12 $self->[XMLNS_11] = $options->{xmlns_11} if defined $options->{xmlns_11};
45 5         14 return bless $self, $class;
46             }
47              
48             #-------------------------------------------------------------------#
49             # reset() - return to the original state (for reuse)
50             #-------------------------------------------------------------------#
51             sub reset {
52 1     1 1 2 my $self = shift;
53 1         8 $#{$self->[NSMAP]} = 0;
  1         5  
54             }
55              
56             #-------------------------------------------------------------------#
57             # push_context() - add a new empty context to the stack
58             #-------------------------------------------------------------------#
59             sub push_context {
60 8     8 1 27 my $self = shift;
61 8         20 push @{$self->[NSMAP]}, [
62             $self->[NSMAP]->[-1]->[DEFAULT],
63 8         27 { %{$self->[NSMAP]->[-1]->[PREFIX_MAP]} },
  8         39  
64             [],
65             ];
66             }
67              
68             #-------------------------------------------------------------------#
69             # pop_context() - remove the topmost context from the stack
70             #-------------------------------------------------------------------#
71             sub pop_context {
72 3     3 1 6 my $self = shift;
73 3 50       5 die 'Trying to pop context without push context' unless @{$self->[NSMAP]} > 1;
  3         10  
74 3         4 pop @{$self->[NSMAP]};
  3         8  
75             }
76              
77             #-------------------------------------------------------------------#
78             # declare_prefix() - declare a prefix in the current scope
79             #-------------------------------------------------------------------#
80             sub declare_prefix {
81 18     18 1 315 my $self = shift;
82 18         25 my $prefix = shift;
83 18         32 my $value = shift;
84              
85 18 50 66     48 warn <<' EOWARN' unless defined $prefix or $self->[AUTO_PREFIX];
86             Prefix was undefined.
87             If you wish to set the default namespace, use the empty string ''.
88             If you wish to autogenerate prefixes, set the auto_prefix option
89             to a true value.
90             EOWARN
91              
92 1     1   6 no warnings 'uninitialized';
  1         2  
  1         93  
93 18 50 33     103 if ($prefix eq 'xml' and $value ne $NS_XML) {
    50 33        
    50 33        
94 0         0 die "The xml prefix can only be bound to the $NS_XML namespace."
95             }
96             elsif ($value eq $NS_XML and $prefix ne 'xml') {
97 0         0 die "the $NS_XML namespace can only be bound to the xml prefix.";
98             }
99             elsif ($value eq $NS_XML and $prefix eq 'xml') {
100 0         0 return 1;
101             }
102 18 100       53 return 0 if index(lc($prefix), 'xml') == 0;
103 1     1   5 use warnings 'uninitialized';
  1         2  
  1         991  
104              
105 17 100 100     70 if (defined $prefix and $prefix eq '') {
106 4         12 $self->[NSMAP]->[-1]->[DEFAULT] = $value;
107             }
108             else {
109 13 100 100     43 die "Cannot declare prefix $prefix" if $value eq '' and not $self->[XMLNS_11];
110 12 100 66     52 if (not defined $prefix and $self->[AUTO_PREFIX]) {
    50 33        
111 1         2 while (1) {
112 1         4 $prefix = $self->[UNKNOWN_PREF]++;
113 1 50       5 last if not exists $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix};
114             }
115             }
116             elsif (not defined $prefix and not $self->[AUTO_PREFIX]) {
117 0         0 return 0;
118             }
119 12         24 $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix} = $value;
120             }
121 16         26 push @{$self->[NSMAP]->[-1]->[DECLARATIONS]}, $prefix;
  16         32  
122 16         41 return 1;
123             }
124              
125             #-------------------------------------------------------------------#
126             # declare_prefixes() - declare several prefixes in the current scope
127             #-------------------------------------------------------------------#
128             sub declare_prefixes {
129 1     1 1 6 my $self = shift;
130 1         4 my %prefixes = @_;
131 1         5 while (my ($k,$v) = each %prefixes) {
132 2         5 $self->declare_prefix($k,$v);
133             }
134             }
135              
136             #-------------------------------------------------------------------#
137             # undeclare_prefix
138             #-------------------------------------------------------------------#
139             sub undeclare_prefix {
140 4     4 1 9 my $self = shift;
141 4         8 my $prefix = shift;
142              
143 4 100 100     23 return if not defined $prefix or $prefix eq '';
144 2 100       8 return unless exists $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix};
145              
146 1         3 my ( $tfix ) = grep { $_ eq $prefix } @{$self->[NSMAP]->[-1]->[DECLARATIONS]};
  2         6  
  1         3  
147 1 50       3 if ( not defined $tfix ) {
148 0         0 die "prefix $prefix not declared in this context\n";
149             }
150              
151 1         2 @{$self->[NSMAP]->[-1]->[DECLARATIONS]} = grep { $_ ne $prefix } @{$self->[NSMAP]->[-1]->[DECLARATIONS]};
  1         3  
  2         5  
  1         3  
152 1         3 delete $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix};
153             }
154              
155             #-------------------------------------------------------------------#
156             # get_prefix() - get a (random) prefix for a given URI
157             #-------------------------------------------------------------------#
158             sub get_prefix {
159 4     4 1 12 my $self = shift;
160 4         7 my $uri = shift;
161              
162             # we have to iterate over the whole hash here because if we don't
163             # the iterator isn't reset and the next pass will fail
164 4         8 my $pref;
165 4         8 while (my ($k, $v) = each %{$self->[NSMAP]->[-1]->[PREFIX_MAP]}) {
  19         65  
166 15 100       43 $pref = $k if $v eq $uri;
167             }
168 4         22 return $pref;
169             }
170              
171             #-------------------------------------------------------------------#
172             # get_prefixes() - get all the prefixes for a given URI
173             #-------------------------------------------------------------------#
174             sub get_prefixes {
175 8     8 1 752 my $self = shift;
176 8         12 my $uri = shift;
177              
178 8 100       22 return keys %{$self->[NSMAP]->[-1]->[PREFIX_MAP]} unless defined $uri;
  5         41  
179 3         9 return grep { $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$_} eq $uri } keys %{$self->[NSMAP]->[-1]->[PREFIX_MAP]};
  13         42  
  3         12  
180             }
181              
182             #-------------------------------------------------------------------#
183             # get_declared_prefixes() - get all prefixes declared in the last context
184             #-------------------------------------------------------------------#
185             sub get_declared_prefixes {
186 3     3 1 7 my $declarations = $_[0]->[NSMAP]->[-1]->[DECLARATIONS];
187 3 50       10 die "At least one context must be pushed onto stack with push_context()\n",
188             "before calling get_declared_prefixes()"
189             if not defined $declarations;
190 3         4 return @{$_[0]->[NSMAP]->[-1]->[DECLARATIONS]};
  3         17  
191             }
192              
193             #-------------------------------------------------------------------#
194             # get_uri() - get a URI given a prefix
195             #-------------------------------------------------------------------#
196             sub get_uri {
197 9     9 1 19 my $self = shift;
198 9         17 my $prefix = shift;
199              
200 9 50       21 warn "Prefix must not be undef in get_uri(). The emtpy prefix must be ''" unless defined $prefix;
201              
202 9 100       25 return $self->[NSMAP]->[-1]->[DEFAULT] if $prefix eq '';
203 8 100       35 return $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix} if exists $self->[NSMAP]->[-1]->[PREFIX_MAP]->{$prefix};
204 2         7 return undef;
205             }
206              
207             #-------------------------------------------------------------------#
208             # process_name() - provide details on a name
209             #-------------------------------------------------------------------#
210             sub process_name {
211 2     2 1 4 my $self = shift;
212 2         5 my $qname = shift;
213 2         3 my $aflag = shift;
214              
215 2 50       7 if ($self->[FATALS]) {
216 0         0 return( ($self->_get_ns_details($qname, $aflag))[0,2], $qname );
217             }
218             else {
219 2         3 eval { return( ($self->_get_ns_details($qname, $aflag))[0,2], $qname ); }
  2         7  
220             }
221             }
222              
223             #-------------------------------------------------------------------#
224             # process_element_name() - provide details on a element's name
225             #-------------------------------------------------------------------#
226             sub process_element_name {
227 9     9 1 861 my $self = shift;
228 9         16 my $qname = shift;
229              
230 9 100       20 if ($self->[FATALS]) {
231 4         11 return $self->_get_ns_details($qname, 0);
232             }
233             else {
234 5         10 eval { return $self->_get_ns_details($qname, 0); }
  5         12  
235             }
236             }
237              
238              
239             #-------------------------------------------------------------------#
240             # process_attribute_name() - provide details on a attribute's name
241             #-------------------------------------------------------------------#
242             sub process_attribute_name {
243 8     8 1 857 my $self = shift;
244 8         14 my $qname = shift;
245              
246 8 100       20 if ($self->[FATALS]) {
247 4         14 return $self->_get_ns_details($qname, 1);
248             }
249             else {
250 4         6 eval { return $self->_get_ns_details($qname, 1); }
  4         9  
251             }
252             }
253              
254              
255             #-------------------------------------------------------------------#
256             # ($ns, $prefix, $lname) = $self->_get_ns_details($qname, $f_attr)
257             # returns ns, prefix, and lname for a given attribute name
258             # >> the $f_attr flag, if set to one, will work for an attribute
259             #-------------------------------------------------------------------#
260             sub _get_ns_details {
261 19     19   28 my $self = shift;
262 19         28 my $qname = shift;
263 19         27 my $aflag = shift;
264              
265 19         30 my ($ns, $prefix, $lname);
266 19 100       88 (my ($tmp_prefix, $tmp_lname) = split /:/, $qname, 3)
267             < 3 or die "Invalid QName: $qname";
268              
269             # no prefix
270 18         38 my $cur_map = $self->[NSMAP]->[-1];
271 18 100       38 if (not defined($tmp_lname)) {
272 5         9 $prefix = undef;
273 5         7 $lname = $qname;
274             # attr don't have a default namespace
275 5 100       12 $ns = ($aflag) ? undef : $cur_map->[DEFAULT];
276             }
277              
278             # prefix
279             else {
280 13 100       29 if (exists $cur_map->[PREFIX_MAP]->{$tmp_prefix}) {
281 9         13 $prefix = $tmp_prefix;
282 9         14 $lname = $tmp_lname;
283 9         15 $ns = $cur_map->[PREFIX_MAP]->{$prefix}
284             }
285             else { # no ns -> lname == name, all rest undef
286 4         33 die "Undeclared prefix: $tmp_prefix";
287             }
288             }
289              
290 14         65 return ($ns, $prefix, $lname);
291             }
292              
293             #-------------------------------------------------------------------#
294             # parse_jclark_notation() - parse the Clarkian notation
295             #-------------------------------------------------------------------#
296             sub parse_jclark_notation {
297 2     2 0 746 shift;
298 2         4 my $jc = shift;
299 2         9 $jc =~ m/^\{(.*)\}([^}]+)$/;
300 2         9 return $1, $2;
301             }
302              
303              
304             #-------------------------------------------------------------------#
305             # Java names mapping
306             #-------------------------------------------------------------------#
307             *XML::NamespaceSupport::pushContext = \&push_context;
308             *XML::NamespaceSupport::popContext = \&pop_context;
309             *XML::NamespaceSupport::declarePrefix = \&declare_prefix;
310             *XML::NamespaceSupport::declarePrefixes = \&declare_prefixes;
311             *XML::NamespaceSupport::getPrefix = \&get_prefix;
312             *XML::NamespaceSupport::getPrefixes = \&get_prefixes;
313             *XML::NamespaceSupport::getDeclaredPrefixes = \&get_declared_prefixes;
314             *XML::NamespaceSupport::getURI = \&get_uri;
315             *XML::NamespaceSupport::processName = \&process_name;
316             *XML::NamespaceSupport::processElementName = \&process_element_name;
317             *XML::NamespaceSupport::processAttributeName = \&process_attribute_name;
318             *XML::NamespaceSupport::parseJClarkNotation = \&parse_jclark_notation;
319             *XML::NamespaceSupport::undeclarePrefix = \&undeclare_prefix;
320              
321              
322             1;
323              
324             __END__