File Coverage

blib/lib/XML/NamespaceSupport.pm
Criterion Covered Total %
statement 130 158 82.2
branch 46 64 71.8
condition 14 24 58.3
subroutine 25 28 89.2
pod 14 15 93.3
total 229 289 79.2


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