File Coverage

blib/lib/RDF/Trine/NamespaceMap.pm
Criterion Covered Total %
statement 85 86 98.8
branch 26 30 86.6
condition 6 7 85.7
subroutine 16 16 100.0
pod 9 9 100.0
total 142 148 95.9


line stmt bran cond sub pod time code
1             # RDF::Trine::NamespaceMap
2             # -----------------------------------------------------------------------------
3              
4              
5             =head1 NAME
6              
7             RDF::Trine::NamespaceMap - Collection of Namespaces
8              
9             =head1 VERSION
10              
11             This document describes RDF::Trine::NamespaceMap version 1.018
12              
13             =head1 SYNOPSIS
14              
15             use RDF::Trine::NamespaceMap;
16             my $map = RDF::Trine::NamespaceMap->new( \%namespaces );
17             $serializer->serialize_model_to_string( $model, namespaces => $map );
18              
19             $map->add_mapping( foaf => 'http://xmlns.com/foaf/0.1/' );
20             my $foaf_namespace = $map->foaf;
21             my $foaf_person = $map->foaf('Person');
22              
23             =head1 DESCRIPTION
24              
25             This module provides an object to manage multiple namespaces for
26             creating L<RDF::Trine::Node::Resource> objects and for serializing.
27              
28             =head1 METHODS
29              
30             =over 4
31              
32             =cut
33              
34             package RDF::Trine::NamespaceMap;
35              
36 68     68   416 use strict;
  68         164  
  68         1728  
37 68     68   335 use warnings;
  68         145  
  68         1694  
38 68     68   309 no warnings 'redefine';
  68         144  
  68         2063  
39 68     68   343 use Scalar::Util qw(blessed);
  68         158  
  68         2709  
40 68     68   384 use Data::Dumper;
  68         156  
  68         3449  
41              
42             ######################################################################
43              
44             our ($VERSION);
45             BEGIN {
46 68     68   50471 $VERSION = '1.018';
47             }
48              
49             ######################################################################
50              
51             =item C<< new ( [ \%namespaces ] ) >>
52              
53             Returns a new namespace map object. You can pass a hash reference with
54             mappings from local names to namespace URIs (given as string or
55             L<RDF::Trine::Node::Resource>).
56              
57             =cut
58              
59             sub new {
60 385     385 1 1980 my $class = shift;
61 385   100     1734 my $map = shift || {};
62 385         1627 my $self = bless( { fwd => {}, rev => {} }, $class );
63 385         1345 foreach my $name ( keys %$map ) {
64 7         44 $self->add_mapping( $name => $map->{$name} );
65             }
66 385         1453 return $self;
67             }
68              
69             =item C<< add_mapping ( $name => $uri ) >>
70              
71             Adds a new namespace to the map. The namespace URI can be passed
72             as string or some object, that provides an uri_value method.
73              
74             =cut
75              
76             sub add_mapping {
77 266     266 1 510 my $self = shift;
78 266         592 my $name = shift;
79 266 50       869 if ($name =~ /^(new|uri|can|isa|VERSION|DOES)$/) {
80             # reserved names
81 0         0 throw RDF::Trine::Error::MethodInvocationError
82             -text => "Cannot use reserved name '$name' as a namespace prefix";
83             }
84              
85 266         534 my $ns = shift;
86 266         679 foreach (qw(1 2)) {
87             # loop twice because the first call to C<<uri_value>> might
88             # return a RDF::Trine::Namespace. Calling C<<uri_value>> on
89             # the namespace object will then return a URI string value.
90 532 100 66     1791 if (blessed($ns) and $ns->can('uri_value')) {
91 8         22 $ns = $ns->uri_value;
92             }
93             }
94              
95             # reverse lookup is many-to-one
96 266   100     1799 $self->{rev}{$ns} ||= {};
97             # forward lookup
98             return $self->{fwd}{$name} =
99 266         1725 $self->{rev}{$ns}{$name} = RDF::Trine::Namespace->new($ns);
100             }
101              
102             =item C<< remove_mapping ( $name ) >>
103              
104             Removes a namespace from the map.
105              
106             =cut
107              
108             sub remove_mapping {
109 1     1 1 348 my $self = shift;
110 1         2 my $name = shift;
111 1         3 my $ns = delete $self->{fwd}{$name};
112 1         4 delete $self->{rev}{$ns->uri_value}{$name};
113             }
114              
115             =item C<< namespace_uri ( $name ) >>
116              
117             Returns the namespace object (if any) associated with the given name.
118              
119             =cut
120              
121             sub namespace_uri {
122 2870     2870 1 4886 my $self = shift;
123 2870         4460 my $name = shift;
124 2870         8086 return $self->{fwd}{$name};
125             }
126              
127             =item C<< list_namespaces >>
128              
129             Returns an array of L<RDF::Trine::Namespace> objects with all the namespaces.
130              
131             =cut
132              
133             sub list_namespaces {
134             # this has to be explicit or the context won't work
135 1     1 1 3 my @out = sort { $a cmp $b } values %{$_[0]{fwd}};
  2         20  
  1         6  
136 1         14 return @out;
137             }
138              
139             =item C<< list_prefixes >>
140              
141             Returns an array of prefixes.
142              
143             =cut
144              
145             sub list_prefixes {
146 3     3 1 7 my @out = sort keys %{$_[0]{fwd}};
  3         25  
147 3         16 return @out;
148             }
149              
150             =item C<< uri ( $prefixed_name ) >>
151              
152             Returns a URI (as L<RDF::Trine::Node::Resource>) for an abbreviated
153             string such as 'foaf:Person'.
154              
155             =cut
156              
157             sub uri {
158 5     5 1 388 my $self = shift;
159 5         9 my $abbr = shift;
160 5         10 my $ns;
161 5         7 my $local = "";
162 5 100       24 if ($abbr =~ m/^([^:]*):(.*)$/) {
163 4         13 $ns = $self->{fwd}{ $1 };
164 4         9 $local = $2;
165             } else {
166 1         3 $ns = $self->{fwd}{$abbr};
167             }
168 5 50       24 return unless (blessed($ns));
169 5 100       15 if ($local ne '') {
170             # don't invoke the AUTOLOAD here
171 3         12 return $ns->uri($local);
172             } else {
173 2         9 return $ns->uri_value;
174             }
175             }
176              
177             =item prefix_for C<< uri ($uri) >>
178              
179             Returns the associated prefix (or potentially multiple prefixes, in
180             list context) for the given URI.
181              
182             =cut
183              
184             sub prefix_for {
185 3     3 1 5 my ($self, $uri) = @_;
186 3 50       12 $uri = $uri->uri_value if ref $uri;
187              
188 3         4 my @candidates;
189 3         5 for my $vuri (keys %{$self->{rev}}) {
  3         13  
190 9 100       24 next if length $vuri > length $uri;
191              
192             # candidate namespace must match exactly
193 4         10 my $cns = substr($uri, 0, length $vuri);
194 4 100       14 push @candidates, keys %{$self->{rev}{$vuri}} if $cns eq $vuri;
  2         8  
195             }
196             # my @candidates;
197             # while (my ($k, $v) = each %{$self->{fwd}}) {
198             # my $vuri = $v->uri->uri_value;
199             # # the input should always be longer than the namespace
200             # next if length $vuri > length $uri;
201              
202             # # candidate namespace must match exactly
203             # my $cns = substr($uri, 0, length $vuri);
204             # push @candidates, $k if $cns eq $vuri;
205             # }
206              
207             # make sure this behaves correctly when empty
208 3 100       9 return unless @candidates;
209              
210             # if this returns more than one prefix, take the
211             # shortest/lexically lowest one.
212 2         6 @candidates = sort @candidates;
213              
214 2 50       8 return wantarray ? @candidates : $candidates[0];
215             }
216              
217             =item abbreviate C<< uri ($uri) >>
218              
219             Complement to L</namespace_uri>. Returns the given URI in C<foo:bar>
220             format or C<undef> if it wasn't matched, therefore the idiom
221              
222             my $str = $nsmap->abbreviate($uri_node) || $uri_node->uri_value;
223              
224             may be useful for certain serialization tasks.
225              
226             =cut
227              
228             sub abbreviate {
229 3     3 1 8 my ($self, $uri) = @_;
230 3 100       11 $uri = $uri->uri_value if ref $uri;
231 3         10 my $prefix = $self->prefix_for($uri);
232              
233             # XXX is this actually the most desirable behaviour?
234 3 100       10 return unless defined $prefix;
235              
236 2         7 my $offset = length $self->namespace_uri($prefix)->uri->uri_value;
237              
238 2         7 return sprintf('%s:%s', $prefix, substr($uri, $offset));
239             }
240              
241              
242             sub AUTOLOAD {
243 390     390   13972 my $self = shift;
244 390         774 our $AUTOLOAD;
245 390 100       6096 return if ($AUTOLOAD =~ /:DESTROY$/);
246 5         25 my ($name) = ($AUTOLOAD =~ m/^.*:(.*)$/);
247 5         14 my $ns = $self->{fwd}{$name};
248 5 100       24 return unless (blessed($ns));
249 4 100       12 if (scalar(@_)) {
250 2         5 my $local = shift(@_);
251 2         17 return $ns->$local( @_ );
252             } else {
253 2         6 return $ns;
254             }
255             }
256              
257             1; # Magic true value required at end of module
258             __END__
259              
260             =back
261              
262             =head1 WARNING
263              
264             Avoid using the names 'can', 'isa', 'VERSION', and 'DOES' as namespace prefix,
265             because these names are defined as method for every Perl object by default.
266             The method names 'new' and 'uri' are also forbidden.
267              
268             =head1 BUGS
269              
270             Please report any bugs or feature requests to through the GitHub web interface
271             at L<https://github.com/kasei/perlrdf/issues>.
272              
273             =head1 AUTHOR
274              
275             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
276              
277             =head1 COPYRIGHT
278              
279             Copyright (c) 2006-2012 Gregory Todd Williams. This
280             program is free software; you can redistribute it and/or modify it under
281             the same terms as Perl itself.
282              
283             =cut