File Coverage

lib/Data/Identifier.pm
Criterion Covered Total %
statement 246 458 53.7
branch 141 344 40.9
condition 77 223 34.5
subroutine 26 41 63.4
pod 24 24 100.0
total 514 1090 47.1


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             # Copyright (c) 2023-2026 Philipp Schafft
4              
5             # licensed under Artistic License 2.0 (see LICENSE file)
6              
7             # ABSTRACT: format independent identifier object
8              
9              
10             package Data::Identifier;
11              
12 7     7   1140619 use v5.20;
  7         19  
13 7     7   26 use strict;
  7         9  
  7         133  
14 7     7   24 use warnings;
  7         16  
  7         310  
15              
16 7     7   21 use parent qw(Data::Identifier::Interface::Known Data::Identifier::Interface::Userdata);
  7         9  
  7         45  
17              
18 7     7   321 use Carp;
  7         9  
  7         332  
19 7     7   10427 use Math::BigInt lib => 'GMP';
  7         231397  
  7         54  
20 7     7   166865 use URI;
  7         33517  
  7         2435  
21              
22             our $VERSION = v0.31;
23              
24             use constant {
25 7         1111 RE_UUID => qr/^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}\z/,
26             RE_OID => qr/^[0-2](?:\.(?:0|[1-9][0-9]*))+\z/,
27             RE_URI => qr/^[a-zA-Z][a-zA-Z0-9\+\.\-]+:/,
28             RE_UINT => qr/^(?:0|[1-9][0-9]*)\z/,
29             RE_SINT => qr/^(?:0|-?[1-9][0-9]*)\z/,
30             RE_QID => qr/^[QPL][1-9][0-9]*\z/,
31             RE_DOI => qr/^10\.[1-9][0-9]+(?:\.[0-9]+)*\/./,
32             RE_GTIN => qr/^[0-9]{8}(?:[0-9]{4,6})?\z/,
33             RE_UNICODE => qr/^U\+([0-9A-F]{4,7})\z/,
34             RE_SIMPLE_TAG => qr/^[^\p{upper case}\s]+\z/,
35 7     7   48 };
  7         7  
36              
37             use constant {
38 7         43008 WK_NULL => '00000000-0000-0000-0000-000000000000', # NULL, undef, ...
39             WK_UUID => '8be115d2-dc2f-4a98-91e1-a6e3075cbc31', # uuid
40             WK_OID => 'd08dc905-bbf6-4183-b219-67723c3c8374', # oid
41             WK_URI => 'a8d1637d-af19-49e9-9ef8-6bc1fbcf6439', # uri
42             WK_SID => 'f87a38cb-fd13-4e15-866c-e49901adbec5', # small-identifier
43             WK_WD => 'ce7aae1e-a210-4214-926a-0ebca56d77e3', # wikidata-identifier
44             WK_GTIN => '82d529be-0f00-4b4f-a43f-4a22de5f5312', # gtin
45             WK_IBAN => 'b1418262-6bc9-459c-b4b0-a054d77db0ea', # iban
46             WK_BIC => 'c8a3a132-f160-473c-b5f3-26a748f37e62', # bic
47             WK_DOI => '931f155e-5a24-499b-9fbb-ed4efefe27fe', # doi
48             WK_FC => 'd576b9d1-47d4-43ae-b7ec-bbea1fe009ba', # factgrid-identifier
49             WK_UNICODE_CP => '5f167223-cc9c-4b2f-9928-9fe1b253b560', # unicode-code-point
50             WK_SNI => '039e0bb7-5dd3-40ee-a98c-596ff6cce405', # sirtx-numerical-identifier
51             WK_HDI => 'f8eb04ef-3b8a-402c-ad7c-1e6814cb1998', # host-defined-identifier
52             WK_UDI => '05af99f9-4578-4b79-aabe-946d8e6f5888', # user-defined-identifier
53             WK_CHAT0W => '2c7e15ed-aa2f-4e2f-9a1d-64df0c85875a', # chat-0-word-identifier
54              
55             NS_WD => '9e10aca7-4a99-43ac-9368-6cbfa43636df', # Wikidata-namespace
56             NS_FC => '6491f7a9-0b29-4ef1-992c-3681cea18182', # factgrid-namespace
57             NS_INT => '5dd8ddbb-13a8-4d6c-9264-36e6dd6f9c99', # integer-namespace
58             NS_DATE => 'fc43fbba-b959-4882-b4c8-90a288b7d416', # gregorian-date-namespace
59             NS_GTIN => 'd95d8b1f-5091-4642-a6b0-a585313915f1', # gtin-namespace
60             NS_UNICODE_CP => '132aa723-a373-48bf-a88d-69f1e00f00cf', # unicode-character-namespace
61 7     7   33 };
  7         11  
62              
63             # Features:
64             my $enabled_oid = 1;
65              
66             my %uuid_to_uriid_org = (
67             WK_UUID() => 'uuid',
68             WK_OID() => 'oid',
69             WK_URI() => 'uri',
70             WK_SID() => 'sid',
71             WK_GTIN() => 'gtin',
72             WK_WD() => 'wikidata-identifier',
73             );
74              
75             my %uuid_org_to_uuid = map {$uuid_to_uriid_org{$_} => $_} keys %uuid_to_uriid_org;
76              
77             my $well_known_uuid = __PACKAGE__->new(ise => WK_UUID, validate => RE_UUID);
78              
79             my %well_known = (
80             uuid => $well_known_uuid,
81             oid => __PACKAGE__->new($well_known_uuid => WK_OID, validate => RE_OID),
82             uri => __PACKAGE__->new($well_known_uuid => WK_URI, validate => RE_URI),
83             sid => __PACKAGE__->new($well_known_uuid => WK_SID, validate => RE_UINT),
84             sni => __PACKAGE__->new($well_known_uuid => WK_SNI, validate => RE_UINT),
85             wd => __PACKAGE__->new($well_known_uuid => WK_WD, validate => RE_QID, generate => 'id-based'),
86             fc => __PACKAGE__->new($well_known_uuid => WK_FC, validate => RE_QID, generate => 'id-based'),
87             gtin => __PACKAGE__->new($well_known_uuid => WK_GTIN, validate => RE_GTIN, generate => 'id-based'),
88             iban => __PACKAGE__->new($well_known_uuid => WK_IBAN),
89             bic => __PACKAGE__->new($well_known_uuid => WK_BIC),
90             doi => __PACKAGE__->new($well_known_uuid => WK_DOI, validate => RE_DOI),
91              
92             # Unofficial, not part of public API:
93             # Also used by Data::Identifier::Util!
94             unicodecp => __PACKAGE__->new($well_known_uuid => WK_UNICODE_CP, validate => RE_UNICODE, generate => 'id-based'),
95              
96             hdi => __PACKAGE__->new($well_known_uuid => WK_HDI, validate => RE_UINT),
97             udi => __PACKAGE__->new($well_known_uuid => WK_UDI, validate => RE_UINT),
98             null => __PACKAGE__->new($well_known_uuid => WK_NULL),
99             );
100              
101             my %registered;
102              
103             $_->register foreach values %well_known;
104              
105             # Refill with namespaces:
106             {
107             my %ns = (
108             wd => NS_WD,
109             fc => NS_FC,
110             gtin => NS_GTIN,
111             unicodecp => NS_UNICODE_CP,
112             );
113              
114             foreach my $wk (keys %ns) {
115             $well_known{$wk}->{namespace} //= Data::Identifier->new(ise => $ns{$wk})->register;
116             }
117             }
118              
119             # Refill with sids:
120             {
121             my %wk_sids = (
122             WK_NULL() => 0, # NULL
123             'ddd60c5c-2934-404f-8f2d-fcb4da88b633' => 1, # also-shares-identifier
124             WK_UUID() => 2,
125             'bfae7574-3dae-425d-89b1-9c087c140c23' => 3, # tagname
126             '7f265548-81dc-4280-9550-1bd0aa4bf748' => 4, # has-type
127             WK_URI() => 5,
128             WK_OID() => 6,
129             # Unassigned: 7
130             'd0a4c6e2-ce2f-4d4c-b079-60065ac681f1' => 8, # language-tag-identifier
131             WK_WD() => 9,
132             '923b43ae-a50e-4db3-8655-ed931d0dd6d4' => 10, # specialises
133             'eacbf914-52cf-4192-a42c-8ecd27c85ee1' => 11, # unicode-string
134             '928d02b0-7143-4ec9-b5ac-9554f02d3fb1' => 12, # integer
135             'dea3782c-6bcb-4ce9-8a39-f8dab399d75d' => 13, # unsigned-integer
136             # Unassigned: 14, 15
137             '6ba648c2-3657-47c2-8541-9b73c3a9b2b4' => 16, # default-context
138             '52a516d0-25d8-47c7-a6ba-80983e576c54' => 17, # proto-file
139             '1cd4a6c6-0d7c-48d1-81e7-4e8d41fdb45d' => 18, # final-file-size
140             '6085f87e-4797-4bb2-b23d-85ff7edc1da0' => 19, # text-fragment
141             '4c9656eb-c130-42b7-9348-a1fee3f42050' => 20, # also-list-contains-also
142             '298ef373-9731-491d-824d-b2836250e865' => 21, # proto-message
143             '7be4d8c7-6a75-44cc-94f7-c87433307b26' => 22, # proto-entity
144             '65bb36f2-b558-48af-8512-bca9150cca85' => 23, # proxy-type
145             'a1c478b5-0a85-4b5b-96da-d250db14a67c' => 24, # flagged-as
146             '59cfe520-ba32-48cc-b654-74f7a05779db' => 25, # marked-as
147             '2bffc55d-7380-454e-bd53-c5acd525d692' => 26, # roaraudio-error-number
148             WK_SID() => 27,
149             'd2750351-aed7-4ade-aa80-c32436cc6030' => 28, # also-has-role
150             '11d8962c-0a71-4d00-95ed-fa69182788a8' => 29, # also-has-comment
151             '30710bdb-6418-42fb-96db-2278f3bfa17f' => 30, # also-has-description
152             # Unassigned: 31
153             '448c50a8-c847-4bc7-856e-0db5fea8f23b' => 32, # final-file-encoding
154             '79385945-0963-44aa-880a-bca4a42e9002' => 33, # final-file-hash
155             '3fde5688-6e34-45e9-8f33-68f079b152c8' => 34, # SEEK_SET
156             'bc598c52-642e-465b-b079-e9253cd6f190' => 35, # SEEK_CUR
157             '06aff30f-70e8-48b4-8b20-9194d22fc460' => 36, # SEEK_END
158             '59a5691a-6a19-4051-bc26-8db82c019df3' => 37, # inode
159             WK_CHAT0W() => 112, # chat-0-word-identifier
160             WK_SNI() => 113, # sirtx-numerical-identifier
161             WK_GTIN() => 160,
162             );
163              
164             foreach my $ise (keys %wk_sids) {
165             my $identifier = __PACKAGE__->new(ise => $ise);
166             $identifier->{id_cache} //= {};
167             $identifier->{id_cache}->{WK_SID()} //= $wk_sids{$ise};
168             $identifier->register; # re-register
169             }
170             }
171              
172             # Refill with snis:
173             {
174             my %wk_snis = (
175             WK_NULL() => 0, # NULL
176             '039e0bb7-5dd3-40ee-a98c-596ff6cce405' => 10, # sirtx-numerical-identifier
177             'f87a38cb-fd13-4e15-866c-e49901adbec5' => 115, # small-identifier
178             '2bffc55d-7380-454e-bd53-c5acd525d692' => 116, # roaraudio-error-number
179             WK_CHAT0W() => 118, # chat-0-word-identifier
180             WK_UUID() => 119,
181             WK_OID() => 120,
182             WK_URI() => 121,
183             WK_WD() => 123,
184             );
185              
186             foreach my $ise (keys %wk_snis) {
187             my $identifier = __PACKAGE__->new(ise => $ise);
188             $identifier->{id_cache} //= {};
189             $identifier->{id_cache}->{WK_SNI()} //= $wk_snis{$ise};
190             $identifier->register; # re-register
191             }
192             }
193              
194             # Update NULL:
195             {
196             my $identifier = __PACKAGE__->new(uuid => WK_NULL);
197             $identifier->{id_cache} //= {};
198             foreach my $type (WK_HDI, WK_CHAT0W) {
199             $identifier->{id_cache}->{$type} //= 0;
200             }
201             $identifier->register;
202             }
203              
204             # Some extra tags such as namespaces:
205             foreach my $ise (NS_WD, NS_INT, NS_DATE) {
206             my $identifier = __PACKAGE__->new(ise => $ise);
207             $identifier->register; # re-register
208             }
209              
210             # Refill with tagnames
211             {
212             my %tagnames = (
213             WK_NULL() => 'null',
214             WK_UUID() => 'uuid',
215             WK_OID() => 'oid',
216             WK_URI() => 'uri',
217             WK_SID() => 'small-identifier',
218             WK_WD() => 'wikidata-identifier',
219             WK_GTIN() => 'gtin',
220             WK_IBAN() => 'iban',
221             WK_BIC() => 'bic',
222             WK_DOI() => 'doi',
223             WK_FC() => 'factgrid-identifier',
224             WK_UNICODE_CP() => 'unicode-code-point',
225             WK_SNI() => 'sirtx-numerical-identifier',
226             WK_HDI() => 'host-defined-identifier',
227             WK_UDI() => 'user-defined-identifier',
228             WK_CHAT0W() => 'chat-0-word-identifier',
229             NS_WD() => 'Wikidata-namespace',
230             NS_FC() => 'factgrid-namespace',
231             NS_INT() => 'integer-namespace',
232             NS_DATE() => 'gregorian-date-namespace',
233             NS_UNICODE_CP() => 'unicode-character-namespace',
234              
235             'ddd60c5c-2934-404f-8f2d-fcb4da88b633' => 'also-shares-identifier',
236             'bfae7574-3dae-425d-89b1-9c087c140c23' => 'tagname',
237             '7f265548-81dc-4280-9550-1bd0aa4bf748' => 'has-type',
238             'd0a4c6e2-ce2f-4d4c-b079-60065ac681f1' => 'language-tag-identifier',
239             '923b43ae-a50e-4db3-8655-ed931d0dd6d4' => 'specialises',
240             'eacbf914-52cf-4192-a42c-8ecd27c85ee1' => 'unicode-string',
241             '928d02b0-7143-4ec9-b5ac-9554f02d3fb1' => 'integer',
242             'dea3782c-6bcb-4ce9-8a39-f8dab399d75d' => 'unsigned-integer',
243             '6ba648c2-3657-47c2-8541-9b73c3a9b2b4' => 'default-context',
244             '52a516d0-25d8-47c7-a6ba-80983e576c54' => 'proto-file',
245             '1cd4a6c6-0d7c-48d1-81e7-4e8d41fdb45d' => 'final-file-size',
246             '6085f87e-4797-4bb2-b23d-85ff7edc1da0' => 'text-fragment',
247             '4c9656eb-c130-42b7-9348-a1fee3f42050' => 'also-list-contains-also',
248             '298ef373-9731-491d-824d-b2836250e865' => 'proto-message',
249             '7be4d8c7-6a75-44cc-94f7-c87433307b26' => 'proto-entity',
250             '65bb36f2-b558-48af-8512-bca9150cca85' => 'proxy-type',
251             'a1c478b5-0a85-4b5b-96da-d250db14a67c' => 'flagged-as',
252             '59cfe520-ba32-48cc-b654-74f7a05779db' => 'marked-as',
253             '2bffc55d-7380-454e-bd53-c5acd525d692' => 'roaraudio-error-number',
254             'd2750351-aed7-4ade-aa80-c32436cc6030' => 'also-has-role',
255             '11d8962c-0a71-4d00-95ed-fa69182788a8' => 'also-has-comment',
256             '30710bdb-6418-42fb-96db-2278f3bfa17f' => 'also-has-description',
257             '448c50a8-c847-4bc7-856e-0db5fea8f23b' => 'final-file-encoding',
258             '79385945-0963-44aa-880a-bca4a42e9002' => 'final-file-hash',
259             '3fde5688-6e34-45e9-8f33-68f079b152c8' => 'SEEK_SET',
260             'bc598c52-642e-465b-b079-e9253cd6f190' => 'SEEK_CUR',
261             '06aff30f-70e8-48b4-8b20-9194d22fc460' => 'SEEK_END',
262             '59a5691a-6a19-4051-bc26-8db82c019df3' => 'inode',
263             '53863a15-68d4-448d-bd69-a9b19289a191' => 'unsigned-integer-generator',
264             'e8aa9e01-8d37-4b4b-8899-42ca0a2a906f' => 'signed-integer-generator',
265             'd74f8c35-bcb8-465c-9a77-01010e8ed25c' => 'unicode-character-generator',
266             '55febcc4-6655-4397-ae3d-2353b5856b34' => 'rgb-colour-generator',
267             '97b7f241-e1c5-4f02-ae3c-8e31e501e1dc' => 'date-generator',
268             '19659233-0a22-412c-bdf1-8ee9f8fc4086' => 'multiplicity-generator',
269             '5ec197c3-1406-467c-96c7-4b1a6ec2c5c9' => 'minimum-multiplicity-generator',
270             );
271              
272             foreach my $ise (keys %tagnames) {
273             my $identifier = __PACKAGE__->new(ise => $ise);
274             $identifier->{tagname} //= [$tagnames{$ise}];
275             $identifier->register; # re-register
276             }
277             }
278              
279             {
280             # ISE -> namespace
281             my %namespaces_uint = (
282             '4a7fc2e2-854b-42ec-b24f-c7fece371865' => 'ac59062c-6ba2-44de-9f54-09e28f2c0b5c', # e621-post-identifier: e621-post-namespace
283             'a0a4fae2-be6f-4a51-8326-6110ba845a16' => '69b7ff38-ca78-43a8-b9ea-66cb02312eef', # e621-pool-identifier: e621-pool-namespace
284             '6e3590b6-2a0c-4850-a71f-8ba196a52280' => 'b96e5d94-0767-40fa-9864-5977eb507ae0', # danbooru2chanjp-post-identifier: danbooru2chanjp-post-namespace
285             );
286             my %namespaces_sint = (
287             '2bffc55d-7380-454e-bd53-c5acd525d692' => '744eaf4e-ae93-44d8-9ab5-744105222da6', # roaraudio-error-number: roaraudio-error-namespace
288             );
289             my %namespaces_simple_tag = (
290             '6fe0dbf0-624b-48b3-b558-0394c14bad6a' => '3623de4d-0dd4-4236-946a-2613467d50f1', # e621tag: e621tag-namespace
291             'c5632c60-5da2-41af-8b60-75810b622756' => '93f2c36b-8cb6-4f2c-924b-98188f224235', # danbooru2chanjp-tag: danbooru2chanjp-tag-namespace
292             );
293              
294             foreach my $ise (keys %namespaces_uint) {
295             my $identifier = __PACKAGE__->new(ise => $ise);
296             $identifier->{namespace} //= __PACKAGE__->new(ise => $namespaces_uint{$ise});
297             $identifier->{validate} //= RE_UINT;
298             $identifier->{generate} //= 'id-based';
299             $identifier->register; # re-register
300             }
301              
302             foreach my $ise (keys %namespaces_sint) {
303             my $identifier = __PACKAGE__->new(ise => $ise);
304             $identifier->{namespace} //= __PACKAGE__->new(ise => $namespaces_sint{$ise});
305             $identifier->{validate} //= RE_SINT;
306             $identifier->{generate} //= 'id-based';
307             $identifier->register; # re-register
308             }
309              
310             foreach my $ise (keys %namespaces_simple_tag) {
311             my $identifier = __PACKAGE__->new(ise => $ise);
312             $identifier->{namespace} //= __PACKAGE__->new(ise => $namespaces_simple_tag{$ise});
313             $identifier->{validate} //= RE_SIMPLE_TAG;
314             $identifier->{generate} //= 'id-based';
315             $identifier->register; # re-register
316             }
317              
318             # validate => RE_QID, namespace => NS_FC, generate => 'id-based'
319             }
320              
321             # Call this after after we loaded all our stuff and before anyone else will register stuff:
322             __PACKAGE__->_known_provider('wellknown');
323              
324              
325             sub new {
326 1347     1347 1 7802 my ($pkg, $type, $id, %opts) = @_;
327 1347         1454 my $self = bless {};
328              
329 1347 50       1724 croak 'No type given' unless defined $type;
330 1347 50       1581 croak 'No id given' unless defined $id;
331              
332 1347 100 100     3045 if (!ref($type) && $type eq 'from') {
333 128 100       171 if (ref($id)) {
334 14         14 my $from = $id;
335 14 50 0     32 if ($id->isa('Data::Identifier')) {
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
336 14 50       24 if (scalar(keys %opts)) {
337 0         0 $type = $id->type;
338 0         0 $id = $id->id;
339             } else {
340 14         35 return $id;
341             }
342             } elsif ($id->isa('URI')) {
343 0         0 $type = 'uri';
344             } elsif ($id->isa('Mojo::URL')) {
345 0         0 $type = 'uri';
346 0         0 $id = $id->to_string;
347             } elsif ($id->isa('Data::URIID::Result')) {
348 0   0 0   0 $opts{displayname} //= sub { return $from->attribute('displayname', default => undef) };
  0         0  
349 0   0 0   0 $opts{description} //= sub { return $from->attribute('description', default => undef) };
  0         0  
350 0   0 0   0 $opts{displaycolour} //= sub { return $from->attribute('displaycolour', default => undef) };
  0         0  
351 0   0 0   0 $opts{icontext} //= sub { return $from->attribute('icon_text', default => undef) };
  0         0  
352 0         0 $type = $id->id_type;
353 0         0 $id = $id->id;
354             } elsif ($id->isa('Data::URIID::Base') || $id->isa('Data::URIID::Colour') || $id->isa('Data::URIID::Service')) {
355             #$opts{displayname} //= $id->name if $id->isa('Data::URIID::Service');
356 0   0     0 $opts{displayname} //= $id->displayname(default => undef, no_defaults => 1);
357 0 0 0     0 $opts{displaycolour} //= $id if $id->isa('Data::URIID::Colour');
358 0         0 $type = 'ise';
359 0         0 $id = $id->ise;
360             } elsif ($id->isa('Data::TagDB::Tag')) {
361 0   0 0   0 $opts{displayname} //= sub { $from->displayname };
  0         0  
362 0         0 $type = 'ise';
363 0         0 $id = $id->ise;
364             } elsif ($id->isa('File::FStore::File') || $id->isa('File::FStore::Adder') || $id->isa('File::FStore::Base')) {
365 0         0 $type = 'ise';
366 0         0 $id = $id->contentise;
367             } elsif ($id->isa('SIRTX::Datecode')) {
368 0         0 $id = $id->as('Data::Identifier');
369 0 0       0 unless (scalar(keys %opts)) {
370 0         0 return $id->as('Data::Identifier');
371             }
372 0         0 $type = $id->type;
373 0         0 $id = $id->id;
374             } elsif ($id->isa('Business::ISBN')) {
375 0         0 $type = $well_known{gtin};
376 0         0 $id = $id->as_isbn13->as_string([]);
377             } elsif ($id->isa('Data::Identifier::Interface::Simple')) {
378             # TODO: We cannot call $id->as('Data::Identifier') here as much as that would be fun,
379             # as this might in turn call exactly this code again resulting in a deep recursion.
380             # A future version might come up with some trick here.
381 0         0 $type = 'ise';
382 0         0 $id = $id->ise;
383             } elsif ($id->isa('JSON::PP::Boolean') || $id->isa('JSON::XS::Boolean')) {
384 0         0 require Data::Identifier::Util;
385 0         0 return Data::Identifier::Util->from_bool($id);
386             } else {
387 0         0 croak 'Unsupported input data';
388             }
389             } else {
390             # If it's not a ref, try as ise.
391 114         129 $type = 'ise';
392             }
393             }
394              
395 1333 100 100     2580 if (!ref($type) && $type eq 'ise') {
396 968 50       1098 croak 'Undefined identifier but type is ISE' unless defined $id;
397              
398 968 100       3361 if ($id =~ /^[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\z/) { # allow less normalised form than RE_UUID
    50          
    50          
399 956         854 $type = $well_known_uuid;
400              
401             # For bootstrap only.
402 956 100 66     1354 if (!defined($type) && $id eq '8be115d2-dc2f-4a98-91e1-a6e3075cbc31') {
403 7         100 $self->{type} = $well_known_uuid = $type = $self;
404 7         14 $self->{id} = $id;
405             }
406             } elsif ($id =~ RE_OID) {
407 0         0 $type = 'oid';
408             } elsif ($id =~ RE_URI) {
409 12         14 $type = 'uri';
410             } else {
411 0         0 croak 'Not a valid ISE identifier';
412             }
413             }
414              
415 1333 100       1601 unless (ref $type) {
416 278 100       536 if ($type =~ /^[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\z/) { # allow less normalised form than RE_UUID
    100          
417 6         38 $type = $pkg->new(uuid => $type);
418 6         10 $type->register;
419             } elsif ($type eq 'wellknown') {
420 8         15 $self = $well_known{$id};
421 8 50       18 croak 'Unknown well-known' unless defined $self;
422 8         24 return $self;
423             } else {
424 264         312 $type = $well_known{$type};
425             }
426 270 50       339 croak 'Unknown type name' unless defined $type;
427             }
428              
429 1325 50       2517 croak 'Not a valid type' unless $type->isa(__PACKAGE__);
430              
431             # we normalise URIs first as they may then normalised again
432 1325 100 100     2550 if ($type == ($well_known{uri} // 0)) {
433 15         20 my $uri = $id.''; # force stringification
434              
435 15 100 50     113 if ($uri =~ m#^urn:uuid:([0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12})\z#) {
    50          
    50          
    50          
    50          
    100          
436 1         3 $id = $1;
437 1         2 $type = $well_known_uuid;
438             } elsif ($uri =~ m#^urn:oid:([0-2](?:\.(?:0|[1-9][0-9]*))+)\z#) {
439 0         0 $id = $1;
440 0         0 $type = $well_known{oid};
441 0         0 } elsif ($uri =~ m#^urn:isbn:([0-9Xx-]+)\z# && scalar(eval {require Business::ISBN; 1})) {
  0         0  
442 0         0 $id = Business::ISBN->new($1)->as_isbn13->as_string([]);
443 0         0 $type = $well_known{gtin};
444 0   0     0 $self->{id_cache} //= {};
445 0   0     0 $self->{id_cache}{WK_URI()} //= $uri;
446             } elsif ($uri =~ m#^https?://www\.wikidata\.org/entity/([QPL][1-9][0-9]*)\z#) {
447 0         0 $id = $1;
448 0         0 $type = $well_known{wd};
449             } elsif ($uri =~ m#^https?://doi\.org/(10\..+)\z#) {
450 0         0 $id = $1;
451 0         0 $type = $well_known{doi};
452             } elsif ($uri =~ m#^https?://uriid\.org/([^/]+)/[^/]+#) {
453 2         6 my $ptype = $1;
454 2 50 66     13 if (defined($uuid_org_to_uuid{$ptype}) || $ptype =~ RE_UUID) {
455 2         11 my $u = URI->new($uri);
456 2         9209 my @path_segments = $u->path_segments;
457 2 50 33     126 if (scalar(@path_segments) == 3 && $path_segments[0] eq '') {
458 2   66     32 $type = $pkg->new(uuid => ($uuid_org_to_uuid{$path_segments[1]} // $path_segments[1]));
459 2         10 $id = $path_segments[2];
460             }
461             }
462             }
463             }
464              
465 1325 100 50     2085 if ($type == ($well_known_uuid // 0)) {
    100 50        
466 1226         1562 $id = lc($id); # normalise
467             } elsif ($type == ($well_known{oid} // 0)) {
468 1 50       5 if ($id =~ /^2\.25\.([1-9][0-9]*)\z/) {
469 1         4 my $hex = Math::BigInt->new($1)->as_hex;
470 1         433 $hex =~ s/^0x//;
471 1         3 $hex = ('0' x (32 - length($hex))) . $hex;
472 1         18 $hex =~ s/^(.{8})(.{4})(.{4})(.{4})(.{12})\z/$1-$2-$3-$4-$5/;
473 1         2 $type = $well_known_uuid;
474 1         2 $id = $hex;
475             }
476             }
477              
478 1325 100       1709 if (defined(my $v = $registered{$type->uuid}{$id})) {
479 650         1307 return $v;
480             }
481              
482              
483 675 100       907 if (defined $type->{validate}) {
484 666 50       4453 croak 'Identifier did not validate against type' unless $id =~ $type->{validate};
485             }
486              
487 675         1120 $self->{type} = $type;
488 675         791 $self->{id} = $id;
489              
490 675         808 foreach my $key (qw(validate namespace generator request generate displayname displaycolour icontext description)) {
491 6075 100       6806 next unless defined $opts{$key};
492 249   66     573 $self->{$key} //= $opts{$key};
493             }
494              
495 675         641 foreach my $key (qw(namespace generator)) {
496 1350 100       1882 if (defined(my $v = $self->{$key})) {
497 3 50       8 unless (ref $v) {
498 3         26 $self->{$key} = $pkg->new(from => $v)->register;
499             }
500             }
501             }
502              
503 675 50       809 if (defined(my $tagname = $opts{tagname})) {
504 0         0 my %tagnames;
505 0 0       0 $tagname = [$tagname] unless ref $tagname;
506 0         0 %tagnames = map {$_ => undef} grep {defined} @{$tagname};
  0         0  
  0         0  
  0         0  
507 0         0 $tagname = [keys %tagnames];
508 0 0       0 if (scalar(@{$tagname})) {
  0         0  
509 0         0 $self->{tagname} = $tagname;
510             }
511             }
512              
513 675         1311 return bless $self;
514             }
515              
516              
517             #@returns __PACKAGE__
518             sub random {
519 0     0 1 0 my ($pkg, %opts) = @_;
520 0   0     0 my $type = $opts{type} // 'uuid';
521              
522 0 0       0 if (ref $type) {
523 0 0       0 if ($type == $well_known_uuid) {
524 0         0 $type = 'uuid';
525             } else {
526 0         0 croak 'Invalid/Unsupported type';
527             }
528             }
529              
530 0 0 0     0 if ($type ne 'ise' && $type ne 'uuid') {
531 0         0 croak 'Invalid/Unsupported type';
532             }
533              
534 0         0 require Data::Identifier::Generate;
535 0         0 my $uuid = Data::Identifier::Generate->_random(%opts{'sources'});
536 0         0 return $pkg->new(uuid => $uuid, %opts{'displayname'});
537             }
538              
539              
540              
541             #@deprecated
542             sub wellknown {
543 0     0 1 0 my ($pkg, @args) = @_;
544 0         0 return $pkg->known('wellknown', @args);
545             }
546              
547              
548             #@returns __PACKAGE__
549             sub type {
550 64     64 1 2035 my ($self) = @_;
551 64         112 return $self->{type};
552             }
553              
554              
555              
556             sub id {
557 31     31 1 36 my ($self) = @_;
558 31         80 return $self->{id};
559             }
560              
561              
562             sub uuid {
563 8700     8700 1 26032 my ($self, %opts) = @_;
564              
565 8700 100 100     21852 return $self->{id_cache}{WK_UUID()} if !$opts{no_defaults} && defined($self->{id_cache}) && defined($self->{id_cache}{WK_UUID()});
      100        
566              
567 8694 100       10506 if ($self->{type} == $well_known_uuid) {
568 8639         18531 return $self->{id};
569             }
570              
571 55 100       84 unless ($opts{no_defaults}) {
572             # Try to generate a UUID and recheck cache:
573 33         54 $self->_generate;
574 33 100 66     109 return $self->{id_cache}{WK_UUID()} if defined($self->{id_cache}) && defined($self->{id_cache}{WK_UUID()});
575             }
576              
577 46 50       115 return $opts{default} if exists $opts{default};
578 0         0 croak 'Identifier has no valid UUID';
579             }
580              
581             sub oid {
582 1091     1091 1 1169 my ($self, %opts) = @_;
583 1091         1041 my $type = $well_known{oid};
584              
585 1091 100 66     3336 return $self->{id_cache}{WK_OID()} if !$opts{no_defaults} && defined($self->{id_cache}) && defined($self->{id_cache}{WK_OID()});
      100        
586              
587 556 50       750 if ($self->{type} == $type) {
588 0         0 return $self->{id};
589             }
590              
591 556 50       693 unless ($opts{no_defaults}) {
592 556 100       613 if (defined(my $uuid = $self->uuid(default => undef))) {
593 544         1955 return $self->{id_cache}{WK_OID()} = sprintf('2.25.%s', Math::BigInt->new('0x'.$uuid =~ tr/-//dr));
594             }
595             }
596              
597 12 50       26 return $opts{default} if exists $opts{default};
598 0         0 croak 'Identifier has no valid OID';
599             }
600              
601             sub uri {
602 1092     1092 1 1467 my ($self, %opts) = @_;
603 1092         1167 my $type = $well_known{uri};
604              
605 1092 100 66     4145 if (!$opts{no_defaults} && !defined($opts{style}) && defined($self->{id_cache}) && defined($self->{id_cache}{WK_URI()})) {
      100        
      100        
606 527         847 return $self->{id_cache}{WK_URI()};
607             }
608              
609 565 100       743 if ($self->{type} == $type) {
610 12         38 return $self->{id};
611             }
612              
613 553   100     1501 $opts{style} //= 'urn';
614              
615 553 50       677 unless ($opts{no_defaults}) {
616 553 100 66     1428 if ($self->{type} == $well_known{wd}) {
    50 33        
    100 33        
    50          
617 6         20 return $self->{id_cache}{WK_URI()} = sprintf('http://www.wikidata.org/entity/%s', $self->{id});
618             } elsif ($self->{type} == $well_known{doi}) {
619 0         0 return $self->{id_cache}{WK_URI()} = sprintf('https://doi.org/%s', $self->{id});
620             } elsif (defined(my $uuid = $self->uuid(default => undef)) && $opts{style} eq 'urn') {
621 546         1551 return $self->{id_cache}{WK_URI()} = sprintf('urn:uuid:%s', $uuid);
622             } elsif ($enabled_oid && defined(my $oid = $self->oid(default => undef)) && $opts{style} eq 'urn') {
623 0         0 return $self->{id_cache}{WK_URI()} = sprintf('urn:oid:%s', $oid);
624             } else {
625 1         618 my $u = URI->new("https://uriid.org/");
626 1         8398 my $type_uuid = $self->{type}->uuid;
627 1   33     8 $u->path_segments('', $uuid_to_uriid_org{$type_uuid} // $type_uuid, $self->{id});
628 1         124 return $self->{id_cache}{WK_URI()} = $u;
629             }
630             }
631              
632 0 0       0 return $opts{default} if exists $opts{default};
633 0         0 croak 'Identifier has no valid URI';
634             }
635              
636             sub sid {
637 1094     1094 1 1290 my ($self, %opts) = @_;
638 1094         1024 my $type = $well_known{sid};
639 1094 100 100     2760 return $self->{id_cache}{WK_SID()} if defined($self->{id_cache}) && defined($self->{id_cache}{WK_SID()});
640 455 50       646 if ($self->{type} == $type) {
641 0         0 return $self->{id};
642             }
643              
644 455 50       1110 return $opts{default} if exists $opts{default};
645 0         0 croak 'Identifier has no valid SID';
646             }
647              
648              
649              
650             sub ise {
651 143     143 1 174 my ($self, %opts) = @_;
652 143         185 my $type = $self->{type};
653 143         168 my $have_default = exists $opts{default};
654 143         165 my $default = delete $opts{default};
655 143         131 my $value;
656              
657 143 50 33     357 if ($type == $well_known{uuid} || $type == $well_known{oid} || $type == $well_known{uri}) {
      33        
658 143         178 $value = $self->{id};
659             } else {
660 0         0 $opts{default} = undef;
661 0   0     0 $value = $self->uuid(%opts) // $self->oid(%opts) // $self->uri(%opts);
      0        
662             }
663              
664 143 50       979 return $value if defined $value;
665 0 0       0 return $default if $have_default;
666 0         0 croak 'Identifier has no valid ISE';
667             }
668              
669              
670             sub as {
671 130     130 1 199 my ($self, $as, %opts) = @_;
672              
673 130 100 66     260 $as = $opts{rawtype} if $as eq 'raw' && defined($opts{rawtype});
674              
675 130 100 66     139 if (ref($as) && eval {$as->isa(__PACKAGE__)}) {
  6         12  
676 6         11 my $type_uuid = $as->uuid;
677 6         6 my $next_type;
678              
679 6 50       8 return $self->id if $self->type->eq($as);
680              
681 6         7 foreach my $test (qw(uuid oid uri sid)) {
682 24 100       32 if ($as == $well_known{$test}) {
683 3         5 $next_type = $test;
684 3         3 last;
685             }
686             }
687              
688 6 100       7 if (defined $next_type) {
689 3 0 33     7 return $self->{id_cache}{$type_uuid} if !$opts{no_defaults} && defined($self->{id_cache}) && defined($self->{id_cache}{$type_uuid});
      33        
690 3         3 $as = $next_type;
691             } else {
692 3 50 33     17 return $self->{id_cache}{$type_uuid} if defined($self->{id_cache}) && defined($self->{id_cache}{$type_uuid});
693 0 0       0 return $opts{default} if exists $opts{default};
694 0         0 croak 'Unknown/Unsupported as: '.$as;
695             }
696             }
697              
698 127 100 66     224 return $self if ($as =~ /^[A-Z]/ || $as =~ /::/) && eval {$self->isa($as)};
  124   66     401  
699              
700 3 50       12 if ($self->isa('Data::Identifier::Interface::Subobjects')) {
701 0         0 require Data::Identifier::Interface::Subobjects; # Is this required?
702 0   0     0 $opts{$_} //= $self->so_get($_, default => undef) foreach Data::Identifier::Interface::Subobjects->KEYS;
703             }
704              
705 3 50       5 if (defined(my $so = $opts{so})) {
706 0         0 require Data::Identifier::Interface::Subobjects; # Is this required?
707 0   0     0 $opts{$_} //= $so->so_get($_, default => undef) foreach Data::Identifier::Interface::Subobjects->KEYS;
708             }
709              
710 3 50       4 $self = __PACKAGE__->new(from => $self) unless eval {$self->isa(__PACKAGE__)};
  3         12  
711              
712 3 50 33     17 if ($as eq 'uuid' || $as eq 'oid' || $as eq 'uri' || $as eq 'sid' || $as eq 'ise') {
    0 33        
    0 33        
    0 33        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
713 3         7 my $func = $self->can($as);
714 3         7 return $self->$func(%opts);
715             } elsif ($as eq __PACKAGE__) {
716 0         0 return $self;
717             } elsif ($as eq 'URI') {
718 0         0 my $had_default = exists $opts{default};
719 0         0 my $default = delete $opts{default};
720 0         0 my $val = $self->uri(%opts, default => undef);
721              
722 0 0       0 return URI->new($val) if defined $val;
723 0 0       0 if ($had_default) {
724 0 0       0 return $default if ref $default;
725 0         0 return URI->new($default);
726             }
727 0         0 croak 'No value for URI';
728             } elsif ($as eq 'Mojo::URL') {
729 0         0 my $had_default = exists $opts{default};
730 0         0 my $default = delete $opts{default};
731 0         0 my $val = $self->uri(%opts, default => undef);
732              
733 0         0 require Mojo::URL;
734              
735 0 0       0 return Mojo::URL->new($val) if defined $val;
736 0 0       0 if ($had_default) {
737 0 0       0 return $default if ref $default;
738 0         0 return Mojo::URL->new($default);
739             }
740 0         0 croak 'No value for URI';
741             } elsif ($as eq 'Data::URIID::Result' && defined($opts{extractor})) {
742 0         0 return $opts{extractor}->lookup($self->type->uuid => $self->id);
743             } elsif ($as eq 'Data::URIID::Service' && defined($opts{extractor})) {
744 0         0 return $opts{extractor}->service($self->uuid);
745             } elsif ($as eq 'SIRTX::Datecode' && eval {
746 0         0 require SIRTX::Datecode;
747 0         0 SIRTX::Datecode->VERSION(v0.03);
748 0         0 1;
749             }) {
750 0         0 return SIRTX::Datecode->new(from => $self);
751             } elsif ($as eq 'Data::URIID::Colour' && eval {
752 0         0 require Data::URIID;
753 0         0 require Data::URIID::Colour;
754 0         0 Data::URIID::Colour->VERSION(v0.14);
755 0         0 1;
756             }) {
757 0         0 return Data::URIID::Colour->new(from => $self, %opts{qw(extractor db fii store)});
758             } elsif ($as eq 'Data::TagDB::Tag' && defined($opts{db})) {
759 0 0       0 if ($opts{autocreate}) {
760 0         0 return $opts{db}->create_tag($self);
761             } else {
762 0         0 return $opts{db}->tag_by_id($self);
763             }
764             } elsif ($as eq 'File::FStore::File' && defined($opts{store})) {
765 0         0 return scalar($opts{store}->query(ise => $self));
766             } elsif ($as eq 'Business::ISBN' && $self->type->eq('gtin')) {
767 0         0 require Business::ISBN;
768 0         0 my $val = Business::ISBN->new($self->id);
769 0 0 0     0 return $val if defined($val) && $val->is_valid;
770             }
771              
772 0 0       0 return $opts{default} if exists $opts{default};
773 0         0 croak 'Unknown/Unsupported as: '.$as;
774             }
775              
776              
777             sub eq {
778 41     41 1 52 my ($self, $other) = @_;
779              
780 41         44 foreach my $e ($self, $other) {
781 82 100 66     118 if (defined($e) && !scalar(eval {$e->isa(__PACKAGE__)})) {
  82         188  
782 4 50       8 if (defined $well_known{$e}) {
783 0         0 $e = $well_known{$e}
784             } else {
785 4         5 $e = Data::Identifier->new(from => $e);
786             }
787             }
788             }
789              
790 41 50       40 if (defined($self)) {
791 41 50       42 return undef unless defined $other;
792 41 100       114 return 1 if $self == $other;
793 11 50       12 return undef unless $self->type->eq($other->type);
794 11         21 return $self->id eq $other->id;
795             } else {
796 0         0 return !defined($other);
797             }
798             }
799              
800              
801             sub cmp {
802 0     0 1 0 my ($self, $other) = @_;
803              
804 0         0 foreach my $e ($self, $other) {
805 0 0 0     0 if (defined($e) && !scalar(eval {$e->isa(__PACKAGE__)})) {
  0         0  
806 0 0       0 if (defined $well_known{$e}) {
807 0         0 $e = $well_known{$e}
808             } else {
809 0         0 $e = Data::Identifier->new(from => $e);
810             }
811             }
812             }
813              
814 0 0       0 if (defined($self)) {
815 0 0       0 return undef unless defined $other;
816 0 0       0 return 0 if $self == $other;
817 0 0       0 if ((my $r = $self->type->cmp($other->type)) != 0) {
818 0         0 return $r;
819             }
820              
821             {
822 0         0 my $self_id = $self->id;
  0         0  
823 0         0 my $other_id = $other->id;
824              
825 0 0 0     0 if ((my ($sa, $sb) = $self_id =~ /^([^0-9]*)([0-9]+)\z/) && (my ($oa, $ob) = $other_id =~ /^([^0-9]*)([0-9]+)\z/)) {
826 0         0 my $r = $sa cmp $oa;
827 0 0       0 return $r if $r;
828 0         0 return $sb <=> $ob;
829             }
830              
831 0         0 return $self_id cmp $other_id;
832             }
833             } else {
834 0         0 return !defined($other);
835             }
836             }
837              
838              
839             sub null_to_undef {
840 0     0 1 0 my ($self, @opts) = @_;
841              
842 0 0       0 croak 'Stray options passed' if scalar @opts;
843              
844 0 0       0 return undef unless defined $self;
845              
846 0 0       0 unless (eval {$self->isa(__PACKAGE__)}) {
  0         0  
847 0         0 $self = __PACKAGE__->new(from => $self);
848             }
849              
850 0 0       0 return undef if $self->eq('null');
851              
852 0         0 return $self;
853             }
854              
855              
856             sub is_null {
857 0     0 1 0 my ($self, @opts) = @_;
858              
859 0 0       0 croak 'Stray options passed' if scalar @opts;
860              
861 0         0 return !defined Data::Identifier::null_to_undef($self);
862             }
863              
864              
865             #@returns __PACKAGE__
866             sub namespace {
867 9     9 1 15 my ($self, %opts) = @_;
868 9         16 my $has_default = exists $opts{default};
869 9         11 my $default = delete $opts{default};
870              
871 9         12 delete $opts{no_defaults};
872              
873 9 50       19 croak 'Stray options passed' if scalar keys %opts;
874              
875 9 50       36 return $self->{namespace} if defined $self->{namespace};
876              
877 0 0       0 return $default if $has_default;
878              
879 0         0 croak 'No namespace';
880             }
881              
882              
883             #@returns __PACKAGE__
884             sub generator {
885 0     0 1 0 my ($self, %opts) = @_;
886 0         0 my $has_default = exists $opts{default};
887 0         0 my $default = delete $opts{default};
888              
889 0         0 delete $opts{no_defaults};
890              
891 0 0       0 croak 'Stray options passed' if scalar keys %opts;
892              
893 0 0       0 return $self->{generator} if defined $self->{generator};
894              
895 0 0       0 return $default if $has_default;
896              
897 0         0 croak 'No generator';
898             }
899              
900              
901             sub request {
902 0     0 1 0 my ($self, %opts) = @_;
903 0         0 my $has_default = exists $opts{default};
904 0         0 my $default = delete $opts{default};
905              
906 0         0 delete $opts{no_defaults};
907              
908 0 0       0 croak 'Stray options passed' if scalar keys %opts;
909              
910 0 0       0 return $self->{request} if defined $self->{request};
911              
912 0 0       0 return $default if $has_default;
913              
914 0         0 croak 'No request';
915             }
916              
917              
918             #@returns __PACKAGE__
919             sub register {
920 1084     1084 1 1126 my ($self) = @_;
921 1084         1410 $registered{$self->{type}->uuid}{$self->{id}} = $self;
922              
923 1084         1195 foreach my $type_name (qw(uuid oid uri sid)) {
924 4336   50     7230 my $f = $self->can($type_name) || next;
925 4336   100     4493 my $v = $self->$f(default => undef) // next;
926 3857         267972 $registered{$well_known{$type_name}->uuid}{$v} = $self;
927             }
928              
929 1084         878 foreach my $extra (keys %{$self->{id_cache}}) {
  1084         2107  
930 2997   50     3504 my $v = $self->{id_cache}{$extra} // next;
931 2997         3370 $registered{$extra}{$v} = $self;
932             }
933              
934 1084         3874 return $self;
935             }
936              
937              
938              
939             sub displayname {
940 16     16 1 300 my ($self, %opts) = @_;
941              
942 16 100       66 if (defined(my $displayname = $self->{displayname})) {
943 1 50       2 $displayname = $self->$displayname() if ref $displayname;
944              
945             # recheck and return as any of the above conversions could result in $displayname becoming invalid.
946 1 50 33     7 return $displayname if defined($displayname) && length($displayname);
947             }
948              
949 15 100       38 if (defined(my $tagname = $self->tagname(default => undef, no_defaults => 1))) {
950 13         72 return $tagname;
951             }
952              
953 2 50       4 return $self->id.'' unless $opts{no_defaults}; # force stringification.
954 2 50       31 return $opts{default} if exists $opts{default};
955 0         0 croak 'No value for displayname';
956             }
957              
958              
959             sub displaycolour {
960 0     0 1 0 my ($self, %opts) = @_;
961              
962 0 0       0 if (defined(my $value = $self->{displaycolour})) {
963 0 0       0 $value = $self->$value() if ref($value) eq 'CODE';
964              
965             # recheck and return as any of the above conversions could result in $displayname becoming invalid.
966 0 0 0     0 return $value if defined($value) && length($value);
967             }
968              
969 0         0 return $opts{default};
970             }
971             sub icontext {
972 0     0 1 0 my ($self, %opts) = @_;
973              
974 0 0       0 if (defined(my $value = $self->{icontext})) {
975 0 0       0 $value = $self->$value() if ref $value;
976              
977             # recheck and return as any of the above conversions could result in $displayname becoming invalid.
978 0 0 0     0 return $value if defined($value) && length($value);
979             }
980              
981 0         0 return $opts{default};
982             }
983             sub description {
984 0     0 1 0 my ($self, %opts) = @_;
985              
986 0 0       0 if (defined(my $value = $self->{description})) {
987 0 0       0 $value = $self->$value() if ref $value;
988              
989             # recheck and return as any of the above conversions could result in $displayname becoming invalid.
990 0 0 0     0 return $value if defined($value) && length($value);
991             }
992              
993 0         0 return $opts{default};
994             }
995              
996              
997             sub tagname {
998 21     21 1 84 my ($self, %opts) = @_;
999 21         32 my $had_default = exists $opts{default};
1000 21         31 my $default = delete $opts{default};
1001 21         28 my $list = delete $opts{list};
1002              
1003 21         26 delete $opts{no_defaults}; # for compatibility.
1004              
1005 21 50       42 croak 'Stray options passed' if scalar keys %opts;
1006              
1007 21 100       45 if (defined(my $tagname = $self->{tagname})) {
1008 13 50       25 return @{$tagname} if $list;
  0         0  
1009              
1010 13         43 return $tagname->[0];
1011             }
1012              
1013 8 50       12 if ($had_default) {
1014 8 100       22 return @{$default} if $list;
  6         15  
1015 2         5 return $default;
1016             }
1017 0         0 croak 'No value for tagname found';
1018             }
1019              
1020             # ---- Private helpers ----
1021              
1022             sub import {
1023 16     16   816 my ($pkg, $opts) = @_;
1024 16 50       2797 return unless defined $opts;
1025 0 0       0 croak 'Bad options' unless ref($opts) eq 'HASH';
1026              
1027 0 0       0 if (defined(my $disable = $opts->{disable})) {
1028 0 0       0 $disable = [split /\s*,\s*/, $disable] unless ref $disable;
1029 0         0 foreach my $to_disable (@{$disable}) {
  0         0  
1030 0 0       0 if ($to_disable eq 'oid') {
1031 0         0 $enabled_oid = undef;
1032 0         0 undef *oid;
1033             } else {
1034 0         0 croak 'Unknown feature: '.$to_disable;
1035             }
1036             }
1037             }
1038             }
1039              
1040             sub _generate {
1041 33     33   33 my ($self) = @_;
1042 33 100       45 unless (exists $self->{_generate}) {
1043 21         33 my __PACKAGE__ $type = $self->type;
1044              
1045 21 100       75 if (defined(my $generate = $type->{generate})) {
1046 9 50       19 unless (ref $generate) {
1047 9         31 $self->{generate} = $generate = {style => $generate};
1048             }
1049              
1050 9   50     34 $self->{id_cache} //= {};
1051              
1052 9 50       14 if (defined(my __PACKAGE__ $ns = eval {$type->namespace->uuid})) {
  9         60  
1053 9         13 my $style = $generate->{style};
1054 9         10 my $input;
1055              
1056 9 50       35 if ($style eq 'id-based') {
1057 9         36 $input = lc($self->id);
1058             } else {
1059 0         0 croak 'Unsupported generator style';
1060             }
1061              
1062 9 50       17 if (defined $input) {
1063 9         57 require Data::Identifier::Generate;
1064 9         39 $self->{id_cache}{WK_UUID()} = Data::Identifier::Generate->_uuid_v5($ns, $input);
1065             }
1066             }
1067             }
1068             }
1069 33         72 $self->{_generate} = undef;
1070             }
1071              
1072             sub _known_provider {
1073 9     9   22 my ($pkg, $class, %opts) = @_;
1074 9 50       29 croak 'Unsupported options passed' if scalar(keys %opts);
1075              
1076 9 100 33     45 if ($class eq 'wellknown') {
    50          
1077 8         12 state $wellknown = do {
1078 7         40 my %hash = map{$_ => $_} values(%well_known), map {values %{$_}} values(%registered);
  1743         2421  
  49         43  
  49         568  
1079 7         243 [values %hash];
1080             };
1081              
1082 8         24 return ($wellknown, rawtype => __PACKAGE__);
1083             } elsif ($class eq 'registered' || $class eq ':all') {
1084 1         5 my %hash = map{$_ => $_} values(%well_known), map {values %{$_}} values(%registered);
  249         323  
  7         4  
  7         70  
1085 1         39 return ([values %hash], rawtype => __PACKAGE__);
1086             }
1087              
1088 0           croak 'Unsupported class';
1089             }
1090              
1091             1;
1092              
1093             __END__