1------------------------------------------------------------------------------
2--                                                                          --
3--                            Matreshka Project                             --
4--                                                                          --
5--                               XML Processor                              --
6--                                                                          --
7--                        Runtime Library Component                         --
8--                                                                          --
9------------------------------------------------------------------------------
10--                                                                          --
11-- Copyright © 2011-2012, Vadim Godunko <vgodunko@gmail.com>                --
12-- All rights reserved.                                                     --
13--                                                                          --
14-- Redistribution and use in source and binary forms, with or without       --
15-- modification, are permitted provided that the following conditions       --
16-- are met:                                                                 --
17--                                                                          --
18--  * Redistributions of source code must retain the above copyright        --
19--    notice, this list of conditions and the following disclaimer.         --
20--                                                                          --
21--  * Redistributions in binary form must reproduce the above copyright     --
22--    notice, this list of conditions and the following disclaimer in the   --
23--    documentation and/or other materials provided with the distribution.  --
24--                                                                          --
25--  * Neither the name of the Vadim Godunko, IE nor the names of its        --
26--    contributors may be used to endorse or promote products derived from  --
27--    this software without specific prior written permission.              --
28--                                                                          --
29-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS      --
30-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT        --
31-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR    --
32-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT     --
33-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,   --
34-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED --
35-- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR   --
36-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF   --
37-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING     --
38-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS       --
39-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.             --
40--                                                                          --
41------------------------------------------------------------------------------
42--  $Revision: 3001 $ $Date: 2012-05-17 14:03:40 +0400 (Thu, 17 May 2012) $
43------------------------------------------------------------------------------
44with Ada.Containers.Vectors;
45
46with Matreshka.XML_Catalogs.Loader;
47with Matreshka.XML_Catalogs.Normalization;
48
49package body Matreshka.XML_Catalogs.Resolver is
50
51   use Matreshka.XML_Catalogs.Entry_Files;
52   use type League.Strings.Universal_String;
53
54   package Next_Catalog_Vectors is
55     new Ada.Containers.Vectors
56          (Positive,
57           Next_Catalog_Entry_Vectors.Cursor,
58           Next_Catalog_Entry_Vectors."=");
59
60   procedure Resolve_External_Identifier
61    (File         :
62       not null Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_Access;
63     Public_Id    : in out League.Strings.Universal_String;
64     System_Id    : in out League.Strings.Universal_String;
65     Resolved_URI : out League.Strings.Universal_String;
66     Delegate     : out
67       Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access);
68   --  Attempts to resolve external identifier using specified catalog entry
69   --  file.
70
71   procedure Resolve_URI
72    (File         :
73       not null Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_Access;
74     URI          : League.Strings.Universal_String;
75     Resolved_URI : out League.Strings.Universal_String;
76     Delegate     : out
77       Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access);
78   --  Attempts to resolve URI using specified catalog entry file.
79
80   ---------------------------------
81   -- Resolve_External_Identifier --
82   ---------------------------------
83
84   procedure Resolve_External_Identifier
85    (List         :
86       not null
87         Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access;
88     Public_Id    : League.Strings.Universal_String;
89     System_Id    : League.Strings.Universal_String;
90     Resolved_URI : out League.Strings.Universal_String;
91     Success      : out Boolean)
92   is
93      Current_List      :
94        Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access
95          := List;
96      Current_Public_Id : League.Strings.Universal_String := Public_Id;
97      Current_System_Id : League.Strings.Universal_String := System_Id;
98      Delegate          :
99        Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access;
100      Identifier        : League.Strings.Universal_String;
101      Unwrapped         : Boolean;
102
103   begin
104      Success := False;
105      Resolved_URI := League.Strings.Empty_Universal_String;
106
107      --  Normalization and unwrapping.
108
109      --  [XML Catalogs] 7.1.1. Input to the Resolver
110      --
111      --  "If the public identifier is a URN in the publicid namespace ([RFC
112      --  3151]), it is converted into another public identifier by
113      --  "unwrapping" the URN (Section 6.4, “URN "Unwrapping"”). This may be
114      --  done, for example, so that a URN can be specified as the public
115      --  identifier and a URL as the system identifier, in the absence of
116      --  widely deployed URN-resolution facilities."
117
118      Matreshka.XML_Catalogs.Normalization.Unwrap_URN
119       (Current_Public_Id, Identifier, Unwrapped);
120
121      if Unwrapped then
122         Current_Public_Id := Identifier;
123      end if;
124
125      --  [XML Catalogs] 7.1.1. Input to the Resolver
126      --
127      --  "If the system identifier is a URN in the publicid namespace, it is
128      --  converted into a public identifier by "unwrapping" the URN. In this
129      --  case, one of the following must apply:
130      --
131      --  1. No public identifier was provided. Resolution continues as if the
132      --  public identifier constructed by unwrapping the URN was supplied as
133      --  the original public identifier and no system identifier was provided.
134      --
135      --  2. The normalized public identifier provided is lexically identical
136      --  to the public identifier constructed by unwrapping the URN.
137      --  Resolution continues as if the system identifier had not been
138      --  supplied.
139      --
140      --  3. The normalized public identifier provided is different from the
141      --  public identifier constructed by unwrapping the URN. This is an
142      --  error. Applications may recover from this error by discarding the
143      --  system identifier and proceeding with the original public
144      --  identifier."
145
146      Matreshka.XML_Catalogs.Normalization.Unwrap_URN
147       (Current_System_Id, Identifier, Unwrapped);
148
149      if Unwrapped then
150         Current_System_Id.Clear;
151
152         if Current_Public_Id.Is_Empty then
153            Current_Public_Id := Identifier;
154
155         else
156            Current_Public_Id :=
157              Matreshka.XML_Catalogs.Normalization.Normalize_Public_Identifier
158               (Current_Public_Id);
159
160            if Current_Public_Id /= Identifier then
161               --  XXX Error reporting is not implemented yet. KDE's test from
162               --  XmlCatConf require to return empty URI and report resolution
163               --  failure.
164
165               Resolved_URI.Clear;
166               Success := False;
167
168               return;
169            end if;
170         end if;
171
172      else
173         Current_Public_Id :=
174           Matreshka.XML_Catalogs.Normalization.Normalize_Public_Identifier
175            (Current_Public_Id);
176         Current_System_Id :=
177           Matreshka.XML_Catalogs.Normalization.Normalize_System_Identifier
178            (Current_System_Id);
179      end if;
180
181      --  External loop handles delegation processing.
182
183      Delegation : loop
184         --  [XML Catalogs] 7.1.2. Resolution of External Identifiers
185         --
186         --  "1. Resolution begins in the first catalog entry file in the
187         --  current catalog entry file list."
188
189         for J in Current_List.Catalog_Entry_Files.First_Index
190                    ..  Current_List.Catalog_Entry_Files.Last_Index
191         loop
192            Resolve_External_Identifier
193             (Current_List.Catalog_Entry_Files.Element (J),
194              Current_Public_Id,
195              Current_System_Id,
196              Resolved_URI,
197              Delegate);
198
199            if Delegate /= null then
200               exit;
201
202            elsif not Resolved_URI.Is_Empty then
203               Success := True;
204
205               return;
206            end if;
207
208            --  [XML Catalogs] 7.1.2. Resolution of External Identifiers
209            --
210            --  "9. If there are one or more catalog entry files remaining on
211            --  the current catalog entry file list, load the next catalog
212            --  entry file and continue resolution efforts: return to step 2."
213         end loop;
214
215         exit when Delegate = null;
216         --  External identifier not resolved and there is no delegation
217         --  requested, return.
218
219         --  Make requested delegation list to be current list.
220
221         Current_List := Delegate;
222         Delegate := null;
223      end loop Delegation;
224
225      Resolved_URI := System_Id;
226   end Resolve_External_Identifier;
227
228   ---------------------------------
229   -- Resolve_External_Identifier --
230   ---------------------------------
231
232   procedure Resolve_External_Identifier
233    (File         :
234       not null Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_Access;
235     Public_Id    : in out League.Strings.Universal_String;
236     System_Id    : in out League.Strings.Universal_String;
237     Resolved_URI : out League.Strings.Universal_String;
238     Delegate     : out
239       Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access)
240   is
241      Length          : Natural;
242      Inserted        : Boolean;
243      Current_File    :
244        Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_Access;
245      Rewrite_System  :
246        Matreshka.XML_Catalogs.Entry_Files.Rewrite_System_Entry_Access;
247      System_Suffix   :
248        Matreshka.XML_Catalogs.Entry_Files.System_Suffix_Entry_Access;
249      Next_Catalog    :
250        Matreshka.XML_Catalogs.Entry_Files.Next_Catalog_Entry_Access;
251      Delegate_System :
252        Matreshka.XML_Catalogs.Entry_Files.Delegate_System_Entry_Vectors.Vector;
253      Delegate_Public :
254        Matreshka.XML_Catalogs.Entry_Files.Delegate_Public_Entry_Vectors.Vector;
255      Next            :
256        Matreshka.XML_Catalogs.Entry_Files.Next_Catalog_Entry_Vectors.Cursor;
257      Stack           : Next_Catalog_Vectors.Vector;
258
259   begin
260      Current_File := File;
261
262      loop
263         if not System_Id.Is_Empty then
264            --  [XML Catalogs] 7.1.2. Resolution of External Identifiers
265            --
266            --  "2. If a system identifier is provided, and at least one
267            --  matching system entry exists, the (absolutized) value of the
268            --  uri attribute of the first matching system entry is returned."
269
270            for J in Current_File.System_Entries.First_Index
271                       .. Current_File.System_Entries.Last_Index
272            loop
273               if System_Id
274                    = Current_File.System_Entries.Element (J).System_Id
275               then
276                  Resolved_URI := Current_File.System_Entries.Element (J).URI;
277
278                  return;
279               end if;
280            end loop;
281
282            --  [XML Catalogs] 7.1.2. Resolution of External Identifiers
283            --
284            --  "3. If a system identifier is provided, and at least one
285            --  matching rewriteSystem entry exists, rewriting is performed.
286            --
287            --  If more than one rewriteSystem entry matches, the matching
288            --  entry with the longest normalized systemIdStartString value is
289            --  used.
290            --
291            --  Rewriting removes the matching prefix and replaces it with the
292            --  rewrite prefix identified by the matching rewriteSystem entry.
293            --  The rewritten string is returned."
294
295            Length := 0;
296
297            for J in Current_File.Rewrite_System_Entries.First_Index
298                       .. Current_File.Rewrite_System_Entries.Last_Index
299            loop
300               if System_Id.Starts_With
301                   (Current_File.Rewrite_System_Entries.Element (J).System_Id)
302               then
303                  if Length
304                       < Current_File.Rewrite_System_Entries.Element
305                          (J).System_Id.Length
306                  then
307                     Rewrite_System :=
308                       Current_File.Rewrite_System_Entries.Element (J);
309                     Length :=
310                       Current_File.Rewrite_System_Entries.Element
311                        (J).System_Id.Length;
312                  end if;
313               end if;
314            end loop;
315
316            if Rewrite_System /= null then
317               Resolved_URI :=
318                 Rewrite_System.Prefix
319                   & System_Id.Slice
320                      (Rewrite_System.System_Id.Length + 1, System_Id.Length);
321
322               return;
323            end if;
324
325            --  [XML Catalogs] 7.1.2. Resolution of External Identifiers
326            --
327            --  "4.  If a system identifier is provided, and at least one
328            --  matching systemSuffix entry exists, the (absolutized) value of
329            --  the uri attribute of the matching entry with the longest
330            --  normalized systemIdSuffix value is returned."
331
332            Length := 0;
333
334            for J in Current_File.System_Suffix_Entries.First_Index
335                       .. Current_File.System_Suffix_Entries.Last_Index
336            loop
337               if System_Id.Ends_With
338                   (Current_File.System_Suffix_Entries.Element (J).System_Id)
339               then
340                  if Length
341                       < Current_File.System_Suffix_Entries.Element
342                          (J).System_Id.Length
343                  then
344                     System_Suffix :=
345                       Current_File.System_Suffix_Entries.Element (J);
346                     Length :=
347                       Current_File.System_Suffix_Entries.Element
348                        (J).System_Id.Length;
349                  end if;
350               end if;
351            end loop;
352
353            if System_Suffix /= null then
354               Resolved_URI := System_Suffix.URI;
355
356               return;
357            end if;
358
359            --  [XML Catalogs] 7.1.2. Resolution of External Identifiers
360            --
361            --  "5. If a system identifier is provided, and one or more
362            --  delegateSystem entries match, delegation is performed.
363            --
364            --  If delegation is to be performed, a new catalog entry file list
365            --  is generated from the set of all matching delegateSystem
366            --  entries. The (absolutized) value of the catalog attribute of
367            --  each matching delegateSystem entry is inserted into the new
368            --  catalog entry file list such that the delegate entry with the
369            --  longest matching systemIdStartString is first on the list, the
370            --  entry with the second longest match is second, etc.
371            --
372            --  These are the only catalog entry files on the list, the current
373            --  list is not considered for the purpose of delegation. If
374            --  delegation fails to find a match, resolution for this entity
375            --  does not resume with the current list. (A subsequent resolution
376            --  attempt for a different entity begins with the original list;
377            --  in other words the catalog entry file list used for delegation
378            --  is distinct and unrelated to the "normal" catalog entry file
379            --  list.)
380            --
381            --  Catalog resolution restarts using exclusively the catalog entry
382            --  files in this new list and the given system identifier; any
383            --  originally given public identifier is ignored during the
384            --  remainder of the resolution of this external identifier: return
385            --  to step 1."
386
387            for J in Current_File.Delegate_System_Entries.First_Index
388                       .. Current_File.Delegate_System_Entries.Last_Index
389            loop
390               if System_Id.Starts_With
391                   (Current_File.Delegate_System_Entries.Element (J).System_Id)
392               then
393                  Inserted := False;
394
395                  for K in Delegate_System.First_Index
396                             .. Delegate_System.Last_Index
397                  loop
398                     if Current_File.Delegate_System_Entries.Element
399                         (J).System_Id.Length
400                          > Delegate_System.Element (K).System_Id.Length
401                     then
402                        Delegate_System.Insert
403                         (K, Current_File.Delegate_System_Entries.Element (J));
404                        Inserted := True;
405
406                        exit;
407                     end if;
408                  end loop;
409
410                  if not Inserted then
411                     Delegate_System.Append
412                      (Current_File.Delegate_System_Entries.Element (J));
413                  end if;
414               end if;
415            end loop;
416
417            if not Delegate_System.Is_Empty then
418               Delegate :=
419                 new Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List;
420
421               for J in Delegate_System.First_Index
422                          .. Delegate_System.Last_Index
423               loop
424                  Delegate.Catalog_Entry_Files.Append
425                   (Matreshka.XML_Catalogs.Loader.Load
426                     (Delegate_System.Element (J).Catalog,
427                      Current_File.Default_Prefer_Mode));
428               end loop;
429
430               Public_Id.Clear;
431
432               return;
433            end if;
434         end if;
435
436         if not Public_Id.Is_Empty then
437            --  [XML Catalogs] 7.1.2. Resolution of External Identifiers
438            --
439            --  "6. If a public identifier is provided, and at least one
440            --  matching public entry exists, the (absolutized) value of the
441            --  uri attribute of the first matching public entry is returned.
442            --  If a system identifier is also provided as part of the input to
443            --  this catalog lookup, only public entries that occur where the
444            --  prefer setting is public are considered for matching."
445
446            for J in Current_File.Public_Entries.First_Index
447                       .. Current_File.Public_Entries.Last_Index
448            loop
449               if Public_Id = Current_File.Public_Entries.Element (J).Public_Id
450                 and then (System_Id.Is_Empty
451                             or else Current_File.Public_Entries.Element
452                                      (J).Prefer
453                                       = Public)
454               then
455                  Resolved_URI := Current_File.Public_Entries.Element (J).URI;
456
457                  return;
458               end if;
459            end loop;
460
461            --  [XML Catalogs] 7.1.2. Resolution of External Identifiers
462            --
463            --  "7. If a public identifier is provided, and one or more
464            --  delegatePublic entries match, delegation is performed. If a
465            --  system identifier is also provided as part of the input to this
466            --  catalog lookup, only delegatePublic entries that occur where
467            --  the prefer setting is public are considered for matching.
468            --
469            --  If delegation is to be performed, a new catalog entry file list
470            --  is generated from the set of all matching delegatePublic
471            --  entries. The value of the catalog attribute of each matching
472            --  delegatePublic entry is inserted into the new catalog entry
473            --  file list such that the delegate entry with the longest
474            --  matching publicIdStartString is first on the list, the entry
475            --  with the second longest match is second, etc.
476            --
477            --  These are the only catalog entry files on the list, the current
478            --  list is not considered for the purpose of delegation. If
479            --  delegation fails to find a match, resolution for this entity
480            --  does not resume with the current list. (A subsequent resolution
481            --  attempt for a different entity begins with the original list;
482            --  in other words the catalog entry file list used for delegation
483            --  is distinct and unrelated to the "normal" catalog entry file
484            --  list.)
485            --
486            --  Catalog resolution restarts using exclusively the catalog entry
487            --  files in this new list and the given public identifier; any
488            --  originally given system identifier is ignored during the
489            --  remainder of the resolution of this external identifier: return
490            --  to step 1."
491
492            for J in Current_File.Delegate_Public_Entries.First_Index
493                       .. Current_File.Delegate_Public_Entries.Last_Index
494            loop
495               if Public_Id.Starts_With
496                   (Current_File.Delegate_Public_Entries.Element (J).Public_Id)
497                 and then (System_Id.Is_Empty
498                             or else
499                               Current_File.Delegate_Public_Entries.Element
500                                (J).Prefer = Public)
501               then
502                  Inserted := False;
503
504                  for K in Delegate_Public.First_Index
505                             .. Delegate_Public.Last_Index
506                  loop
507                     if Current_File.Delegate_Public_Entries.Element
508                         (J).Public_Id.Length
509                          > Delegate_Public.Element (K).Public_Id.Length
510                     then
511                        Delegate_Public.Insert
512                         (K, Current_File.Delegate_Public_Entries.Element (J));
513                        Inserted := True;
514
515                        exit;
516                     end if;
517                  end loop;
518
519                  if not Inserted then
520                     Delegate_Public.Append
521                      (Current_File.Delegate_Public_Entries.Element (J));
522                  end if;
523               end if;
524            end loop;
525
526            if not Delegate_Public.Is_Empty then
527               Delegate :=
528                 new Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List;
529
530               for J in Delegate_Public.First_Index
531                          .. Delegate_Public.Last_Index
532               loop
533                  Delegate.Catalog_Entry_Files.Append
534                   (Matreshka.XML_Catalogs.Loader.Load
535                     (Delegate_Public.Element (J).Catalog,
536                      Current_File.Default_Prefer_Mode));
537               end loop;
538
539               System_Id.Clear;
540
541               return;
542            end if;
543         end if;
544
545         --  [XML Catalogs] 7.1.2. Resolution of External Identifiers
546         --
547         --  "8. If the current catalog entry file contains one or more
548         --  nextCatalog entries, the catalog entry files referenced by each
549         --  nextCatalog entry's "catalog" attribute are inserted, in the order
550         --  that they appear in this catalog entry file, onto the current
551         --  catalog entry file list, immediately after the current catalog
552         --  entry file.
553
554         Next := Current_File.Next_Catalog_Entries.First;
555
556         if Next_Catalog_Entry_Vectors.Has_Element (Next) then
557            Stack.Append (Next);
558         end if;
559
560         exit when Stack.Is_Empty;
561
562         --  Take next catalog entry file from the stack.
563
564         Next := Stack.Last_Element;
565         Stack.Delete_Last;
566         Next_Catalog := Next_Catalog_Entry_Vectors.Element (Next);
567
568         if Next_Catalog.File = null then
569            Next_Catalog.File :=
570              Matreshka.XML_Catalogs.Loader.Load
571               (Next_Catalog.Catalog, File.Default_Prefer_Mode);
572         end if;
573
574         Current_File := Next_Catalog.File;
575
576         Next_Catalog_Entry_Vectors.Next (Next);
577
578         if Next_Catalog_Entry_Vectors.Has_Element (Next) then
579            Stack.Append (Next);
580         end if;
581      end loop;
582   end Resolve_External_Identifier;
583
584   -----------------
585   -- Resolve_URI --
586   -----------------
587
588   procedure Resolve_URI
589    (List         :
590       not null
591         Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access;
592     URI          : League.Strings.Universal_String;
593     Resolved_URI : out League.Strings.Universal_String;
594     Success      : out Boolean)
595   is
596      Current_List :
597        Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access
598          := List;
599      Delegate     :
600        Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access;
601      Current_URI  : League.Strings.Universal_String;
602      Identifier   : League.Strings.Universal_String;
603      Unwrapped    : Boolean;
604
605   begin
606      Success := False;
607      Resolved_URI := League.Strings.Empty_Universal_String;
608
609      --  [XML Catalogs]  7.2.1. Input to the Resolver
610      --
611      --  "If the URI reference is a URN in the publicid namespace ([RFC
612      --  3151]), it is converted into a public identifier by "unwrapping" the
613      --  URN (Section 6.4, “URN "Unwrapping"”). Resolution continues by
614      --  following the semantics of external identifier resolution (Section
615      --  7.1, “External Identifier Resolution”) as if the public identifier
616      --  constructed by unwrapping the URN had been provided and no system
617      --  identifier had been provided. Otherwise, resolution of the URI
618      --  reference proceeds according to the steps below."
619
620      Matreshka.XML_Catalogs.Normalization.Unwrap_URN
621       (URI, Identifier, Unwrapped);
622
623      if Unwrapped then
624         Resolve_External_Identifier
625          (List,
626           Identifier,
627           League.Strings.Empty_Universal_String,
628           Resolved_URI,
629           Success);
630
631         return;
632      end if;
633
634      Current_URI := Matreshka.XML_Catalogs.Normalization.Normalize_URI (URI);
635
636      --  External loop handles delegation processing.
637
638      Delegation : loop
639         --  [XML Catalogs] 7.2.2. Resolution of URI references
640         --
641         --  "1.  Resolution begins in the first catalog entry file in the
642         --  current catalog list."
643
644         for J in Current_List.Catalog_Entry_Files.First_Index
645                    ..  Current_List.Catalog_Entry_Files.Last_Index
646         loop
647            Resolve_URI
648             (Current_List.Catalog_Entry_Files.Element (J),
649              Current_URI,
650              Resolved_URI,
651              Delegate);
652
653            if Delegate /= null then
654               exit;
655
656            elsif not Resolved_URI.Is_Empty then
657               Success := True;
658
659               return;
660            end if;
661
662            --  [XML Catalogs] 7.2.2. Resolution of URI references
663            --
664            --  "7. If there are one or more catalog entry files remaining on
665            --  the current catalog entry file list, load the next catalog
666            --  entry file and continue resolution efforts: return to step 2."
667         end loop;
668
669         exit when Delegate = null;
670         --  URI is not resolved and there is no delegation requested, return.
671
672         --  Make requested delegation list to be current list.
673
674         Current_List := Delegate;
675         Delegate := null;
676      end loop Delegation;
677
678      Resolved_URI := Current_URI;
679   end Resolve_URI;
680
681   -----------------
682   -- Resolve_URI --
683   -----------------
684
685   procedure Resolve_URI
686    (File         :
687       not null Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_Access;
688     URI          : League.Strings.Universal_String;
689     Resolved_URI : out League.Strings.Universal_String;
690     Delegate     : out
691       Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List_Access)
692   is
693      Length       : Natural;
694      Inserted        : Boolean;
695      Rewrite_URI  :
696        Matreshka.XML_Catalogs.Entry_Files.Rewrite_URI_Entry_Access;
697      URI_Suffix   :
698        Matreshka.XML_Catalogs.Entry_Files.URI_Suffix_Entry_Access;
699      Delegate_URI :
700        Matreshka.XML_Catalogs.Entry_Files.Delegate_URI_Entry_Vectors.Vector;
701
702   begin
703      --  [XML Catalogs] 7.2.2. Resolution of URI references
704      --
705      --  "2. If at least one matching uri entry exists, the (absolutized)
706      --  value of the uri attribute of the first matching uri entry is
707      --  returned."
708
709      for J in File.URI_Entries.First_Index .. File.URI_Entries.Last_Index loop
710         if URI = File.URI_Entries.Element (J).Name then
711            Resolved_URI := File.URI_Entries.Element (J).URI;
712
713            return;
714         end if;
715      end loop;
716
717      --  [XML Catalogs] 7.2.2. Resolution of URI references
718      --
719      --  "3. If at least one matching rewriteURI entry exists, rewriting is
720      --  performed.
721      --
722      --  If more than one rewriteURI entry matches, the matching entry with
723      --  the longest normalized uriStartString value is used.
724      --
725      --  Rewriting removes the matching prefix and replaces it with the
726      --  rewrite prefix identified by the matching rewriteURI entry. The
727      --  rewritten string is returned."
728
729      Length := 0;
730
731      for J in File.Rewrite_URI_Entries.First_Index
732                 .. File.Rewrite_URI_Entries.Last_Index
733      loop
734         if URI.Starts_With (File.Rewrite_URI_Entries.Element (J).Prefix) then
735            if Length < File.Rewrite_URI_Entries.Element (J).Prefix.Length then
736               Rewrite_URI := File.Rewrite_URI_Entries.Element (J);
737               Length := File.Rewrite_URI_Entries.Element (J).Prefix.Length;
738            end if;
739         end if;
740      end loop;
741
742      if Rewrite_URI /= null then
743         Resolved_URI :=
744           Rewrite_URI.Rewrite
745             & URI.Slice (Rewrite_URI.Prefix.Length + 1, URI.Length);
746
747         return;
748      end if;
749
750      --  [XML Catalogs] 7.2.2. Resolution of URI references
751      --
752      --  "4. If at least one matching uriSuffix entry exists, the
753      --  (absolutized) value of the uri attribute of the matching entry with
754      --  the longest normalized uriSuffix value is returned."
755
756      Length := 0;
757
758      for J in File.URI_Suffix_Entries.First_Index
759                 .. File.URI_Suffix_Entries.Last_Index
760      loop
761         if URI.Ends_With (File.URI_Suffix_Entries.Element (J).Suffix) then
762            if Length < File.URI_Suffix_Entries.Element (J).Suffix.Length then
763               URI_Suffix := File.URI_Suffix_Entries.Element (J);
764               Length := File.URI_Suffix_Entries.Element (J).Suffix.Length;
765            end if;
766         end if;
767      end loop;
768
769      if URI_Suffix /= null then
770         Resolved_URI := URI_Suffix.URI;
771
772         return;
773      end if;
774
775      --  [XML Catalogs] 7.2.2. Resolution of URI references
776      --
777      --  "5. If one or more delegateURI entries match, delegation is
778      --  performed.
779      --
780      --  If delegation is to be performed, a new catalog entry file list is
781      --  generated from the set of all matching delegateURI entries. The
782      --  (absolutized) value of the catalog attribute of each matching
783      --  delegateURI entry is inserted into the new catalog entry file list
784      --  such that the delegate entry with the longest matching uriStartString
785      --  is first on the list, the entry with the second longest match is
786      --  second, etc.
787      --
788      --  These are the only catalog entry files on the list, the current list
789      --  is not considered for the purpose of delegation. If delegation fails
790      --  to find a match, resolution for this entity does not resume with the
791      --  current list. (A subsequent resolution attempt for a different entity
792      --  begins with the original list; in other words the catalog entry file
793      --  list used for delegation is distinct and unrelated to the "normal"
794      --  catalog entry file list.)
795      --
796      --  Catalog resolution restarts using exclusively the catalog entry files
797      --  in this new list and the given URI reference: return to step 1.
798
799      for J in File.Delegate_URI_Entries.First_Index
800                 .. File.Delegate_URI_Entries.Last_Index
801      loop
802         if URI.Starts_With (File.Delegate_URI_Entries.Element (J).Prefix) then
803            Inserted := False;
804
805            for K in Delegate_URI.First_Index .. Delegate_URI.Last_Index loop
806               if File.Delegate_URI_Entries.Element (J).Prefix.Length
807                    > Delegate_URI.Element (K).Prefix.Length
808               then
809                  Delegate_URI.Insert
810                   (K, File.Delegate_URI_Entries.Element (J));
811                  Inserted := True;
812
813                  exit;
814               end if;
815            end loop;
816
817            if not Inserted then
818               Delegate_URI.Append
819                (File.Delegate_URI_Entries.Element (J));
820            end if;
821         end if;
822      end loop;
823
824      if not Delegate_URI.Is_Empty then
825         Delegate :=
826           new Matreshka.XML_Catalogs.Entry_Files.Catalog_Entry_File_List;
827
828         for J in Delegate_URI.First_Index .. Delegate_URI.Last_Index loop
829            Delegate.Catalog_Entry_Files.Append
830             (Matreshka.XML_Catalogs.Loader.Load
831               (Delegate_URI.Element (J).Catalog,
832                File.Default_Prefer_Mode));
833         end loop;
834
835         return;
836      end if;
837   end Resolve_URI;
838
839end Matreshka.XML_Catalogs.Resolver;
840