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.017
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   421 use strict;
  68         162  
  68         1704  
37 68     68   325 use warnings;
  68         151  
  68         1647  
38 68     68   322 no warnings 'redefine';
  68         153  
  68         2117  
39 68     68   356 use Scalar::Util qw(blessed);
  68         159  
  68         2785  
40 68     68   398 use Data::Dumper;
  68         165  
  68         3649  
41              
42             ######################################################################
43              
44             our ($VERSION);
45             BEGIN {
46 68     68   50890 $VERSION = '1.017';
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 2071 my $class = shift;
61 385   100     2014 my $map = shift || {};
62 385         1934 my $self = bless( { fwd => {}, rev => {} }, $class );
63 385         1652 foreach my $name ( keys %$map ) {
64 7         45 $self->add_mapping( $name => $map->{$name} );
65             }
66 385         1604 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 634 my $self = shift;
78 266         631 my $name = shift;
79 266 50       1070 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         607 my $ns = shift;
86 266         766 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     2133 if (blessed($ns) and $ns->can('uri_value')) {
91 8         24 $ns = $ns->uri_value;
92             }
93             }
94              
95             # reverse lookup is many-to-one
96 266   100     2151 $self->{rev}{$ns} ||= {};
97             # forward lookup
98             return $self->{fwd}{$name} =
99 266         2458 $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 445 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 5382 my $self = shift;
123 2870         4705 my $name = shift;
124 2870         8720 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         19  
  1         6  
136 1         15 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 9 my @out = sort keys %{$_[0]{fwd}};
  3         24  
147 3         15 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 483 my $self = shift;
159 5         11 my $abbr = shift;
160 5         9 my $ns;
161 5         8 my $local = "";
162 5 100       27 if ($abbr =~ m/^([^:]*):(.*)$/) {
163 4         12 $ns = $self->{fwd}{ $1 };
164 4         8 $local = $2;
165             } else {
166 1         2 $ns = $self->{fwd}{$abbr};
167             }
168 5 50       24 return unless (blessed($ns));
169 5 100       16 if ($local ne '') {
170             # don't invoke the AUTOLOAD here
171 3         11 return $ns->uri($local);
172             } else {
173 2         7 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       9 $uri = $uri->uri_value if ref $uri;
187              
188 3         8 my @candidates;
189 3         5 for my $vuri (keys %{$self->{rev}}) {
  3         12  
190 9 100       22 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       12 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       11 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       7 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       14 $uri = $uri->uri_value if ref $uri;
231 3         9 my $prefix = $self->prefix_for($uri);
232              
233             # XXX is this actually the most desirable behaviour?
234 3 100       11 return unless defined $prefix;
235              
236 2         5 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   33467 my $self = shift;
244 390         706 our $AUTOLOAD;
245 390 100       7198 return if ($AUTOLOAD =~ /:DESTROY$/);
246 5         25 my ($name) = ($AUTOLOAD =~ m/^.*:(.*)$/);
247 5         13 my $ns = $self->{fwd}{$name};
248 5 100       23 return unless (blessed($ns));
249 4 100       13 if (scalar(@_)) {
250 2         5 my $local = shift(@_);
251 2         16 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