with Unicode;           use Unicode;
with Unicode.CES;       use Unicode.CES;
with Sax.Attributes;    use Sax.Attributes;
with Sax.Exceptions;    use Sax.Exceptions;
with Sax.Locators;      use Sax.Locators;
with Sax.Encodings;     use Sax.Encodings;
with Sax.Utils;         use Sax.Utils;
with Sax.Readers;       use Sax.Readers;
with Schema.Validators; use Schema.Validators;
with Ada.Exceptions;    use Ada.Exceptions;
with Ada.Unchecked_Deallocation;
with GNAT.IO;               use GNAT.IO;
with Input_Sources.File;    use Input_Sources.File;
with Schema.Schema_Readers; use Schema.Schema_Readers;

with GNAT.Directory_Operations; use GNAT.Directory_Operations;

package body Schema.Readers is

   Debug : Boolean := False;

   procedure Unchecked_Free is new Ada.Unchecked_Deallocation
     (Validator_List_Record, Validator_List);

   procedure Push  (List    : in out Validator_List;
                    Element : XML_Element;
                    Typ     : XML_Type;
                    G       : XML_Grammar_NS;
                    Data    : Validator_Data;
                    Is_Nil  : Boolean);
   procedure Pop   (List : in out Validator_List);
   procedure Clear (List : in out Validator_List);
   --  Push or remove validators from the list

   pragma Unreferenced (Clear);

   procedure Add_XML_Instance_Attributes
     (Handler   : in out Validating_Reader;
      Validator : access XML_Validator_Record'Class);
   --  Add the standard attributes from the XMLSchema-Instance namespace to
   --  Tmp.

   procedure Parse_Grammars
     (Handler  : in out Validating_Reader;
      Schema_Location : Byte_Sequence);
   --  Parse multiple grammars, as defined by the "schemaLocation" attribute

   procedure Internal_Characters
     (Handler : in out Validating_Reader;
      Ch      : Unicode.CES.Byte_Sequence);
   --  Store Ch in the current sequence of characters. This is needed to
   --  collapse multiple calls to Characters and Ignorable_Whitespace into a
   --  single string, for validation purposes.

   procedure Validate_Current_Characters (Handler : Validating_Reader'Class);
   --  Validate the current set of characters

   procedure Free (Mapping : in out Prefix_Mapping_Access);
   --  Free the memory occupied by Mapping

   procedure Hook_Start_Element
     (Handler       : in out Reader'Class;
      Namespace_URI : Unicode.CES.Byte_Sequence := "";
      Local_Name    : Unicode.CES.Byte_Sequence := "";
      Qname         : Unicode.CES.Byte_Sequence := "";
      Atts          : Sax.Attributes.Attributes'Class);
   procedure Hook_End_Element
     (Handler       : in out Reader'Class;
      Namespace_URI : Unicode.CES.Byte_Sequence := "";
      Local_Name    : Unicode.CES.Byte_Sequence := "";
      Qname         : Unicode.CES.Byte_Sequence := "");
   procedure Hook_Characters
     (Handler : in out Reader'Class; Ch : Unicode.CES.Byte_Sequence);
   procedure Hook_Ignorable_Whitespace
     (Handler : in out Reader'Class;
      Ch      : Unicode.CES.Byte_Sequence);
   procedure Hook_Start_Prefix_Mapping
     (Handler : in out Reader'Class;
      Prefix  : Unicode.CES.Byte_Sequence;
      URI     : Unicode.CES.Byte_Sequence);
   procedure Hook_End_Prefix_Mapping
     (Handler : in out Reader'Class;
      Prefix  : Unicode.CES.Byte_Sequence);
   procedure Hook_Set_Document_Locator
     (Handler : in out Reader'Class;
      Loc     : access Sax.Locators.Locator'Class);
   --  See for the corresponding primitive operations. These provide the
   --  necessary validation hooks.

   ----------
   -- Free --
   ----------

   procedure Free (Mapping : in out Prefix_Mapping_Access) is
      procedure Unchecked_Free is new Ada.Unchecked_Deallocation
        (Prefix_Mapping, Prefix_Mapping_Access);
   begin
      if Mapping /= null then
         Free (Mapping.Prefix);
         Free (Mapping.Namespace);
         Unchecked_Free (Mapping);
      end if;
   end Free;

   ----------------------
   -- Set_Debug_Output --
   ----------------------

   procedure Set_Debug_Output (Output : Boolean) is
   begin
      Debug := Output;
   end Set_Debug_Output;

   ----------------------------
   -- Set_Validating_Grammar --
   ----------------------------

   procedure Set_Validating_Grammar
     (Reader  : in out Validating_Reader;
      Grammar : Schema.Validators.XML_Grammar) is
   begin
      Reader.Grammar := Grammar;
   end Set_Validating_Grammar;

   ----------
   -- Push --
   ----------

   procedure Push
     (List    : in out Validator_List;
      Element : XML_Element;
      Typ     : XML_Type;
      G       : XML_Grammar_NS;
      Data    : Validator_Data;
      Is_Nil  : Boolean) is
   begin
      List := new Validator_List_Record'
        (Element => Element,
         Typ     => Typ,
         Grammar => G,
         Data    => Data,
         Is_Nil  => Is_Nil,
         Start_Loc  => null,
         Characters => null,
         Next    => List);
   end Push;

   ---------
   -- Pop --
   ---------

   procedure Pop (List : in out Validator_List) is
      Tmp : Validator_List := List;
   begin
      if List /= null then
         if List.Start_Loc /= null then
            Unref (List.Start_Loc);
         end if;

         if List.Characters /= null then
            Free (List.Characters);
         end if;

         Free (List.Data);
         List := List.Next;

         Unchecked_Free (Tmp);
      end if;
   end Pop;

   -----------
   -- Clear --
   -----------

   procedure Clear (List : in out Validator_List) is
   begin
      while List /= null loop
         Pop (List);
      end loop;
   end Clear;

   ---------------------------------
   -- Add_XML_Instance_Attributes --
   ---------------------------------

   procedure Add_XML_Instance_Attributes
     (Handler   : in out Validating_Reader;
      Validator : access XML_Validator_Record'Class)
   is
      XML_G, XML_IG : XML_Grammar_NS;
   begin
      Get_NS (Handler.Grammar, XML_Schema_URI, Result => XML_G);
      Get_NS (Handler.Grammar, XML_Instance_URI, Result => XML_IG);

      Add_Attribute
        (Validator,
         Create_Local_Attribute ("type", XML_IG, Lookup (XML_G, "string")));
      Add_Attribute
        (Validator,
         Create_Local_Attribute ("nil", XML_IG, Lookup (XML_G, "boolean")));
      Add_Attribute
        (Validator,
         Create_Local_Attribute ("schemaLocation", XML_IG,
                                 List_Of (Lookup (XML_G, "string"))));
      Add_Attribute
        (Validator,
         Create_Local_Attribute ("noNamespaceSchemaLocation", XML_IG,
                                 Lookup (XML_G, "string")));
   end Add_XML_Instance_Attributes;

   ---------------------
   -- To_Absolute_URI --
   ---------------------

   function To_Absolute_URI
     (Handler : Validating_Reader;
      URI     : Byte_Sequence) return Byte_Sequence is
   begin
      if URI (URI'First) /= '/'
        and then URI (URI'First) /= '\'
      then
         return Dir_Name (Get_System_Id (Handler.Locator.all)) & URI;
      else
         return URI;
      end if;
   end To_Absolute_URI;

   -------------------
   -- Parse_Grammar --
   -------------------

   procedure Parse_Grammar
     (Handler  : in out Validating_Reader;
      Xsd_File : Byte_Sequence;
      Add_To   : in out XML_Grammar)
   is
      File     : File_Input;
      Schema   : Schema_Reader;
      Xsd_File_Full : constant Byte_Sequence :=
        To_Absolute_URI (Handler, Xsd_File);
   begin
      if Debug then
         Put_Line ("Parsing new grammar: " & Xsd_File_Full);
      end if;
      Open (Xsd_File_Full, File);
      Set_Public_Id (File, Xsd_File_Full);
      Set_System_Id (File, Xsd_File_Full);

      Set_Created_Grammar (Schema, Add_To);
      Parse (Schema, File);
      Close (File);
      Add_To := Get_Created_Grammar (Schema);

      if Debug then
         Put_Line ("Done parsing new grammar: " & Xsd_File);
      end if;
   end Parse_Grammar;

   --------------------
   -- Parse_Grammars --
   --------------------

   procedure Parse_Grammars
     (Handler  : in out Validating_Reader;
      Schema_Location : Byte_Sequence)
   is
      Start_NS, Last_NS, Index : Integer;
      Start_XSD, Last_XSD : Integer;
      C : Unicode_Char;
   begin
      Index    := Schema_Location'First;
      Start_NS := Schema_Location'First;
      while Index <= Schema_Location'Last loop
         while Index <= Schema_Location'Last loop
            Last_NS := Index;
            Encoding.Read (Schema_Location, Index, C);
            exit when Is_White_Space (C);
         end loop;

         while Index <= Schema_Location'Last loop
            Start_XSD := Index;
            Encoding.Read (Schema_Location, Index, C);
            exit when not Is_White_Space (C);
         end loop;

         while Index <= Schema_Location'Last loop
            Last_XSD := Index;
            Encoding.Read (Schema_Location, Index, C);
            exit when Is_White_Space (C);
         end loop;

         if Index > Schema_Location'Last then
            Last_XSD := Schema_Location'Last + 1;
         end if;

         if Debug then
            Put_Line ("NS=" & Schema_Location (Start_NS .. Last_NS - 1)
                      & ASCII.LF
                      & "XSD=" & Schema_Location (Start_XSD .. Last_XSD - 1));
         end if;

         Parse_Grammar (Handler, Schema_Location (Start_XSD .. Last_XSD - 1),
                        Add_To => Handler.Grammar);

         while Index <= Schema_Location'Last loop
            Start_NS := Index;
            Encoding.Read (Schema_Location, Index, C);
            exit when not Is_White_Space (C);
         end loop;
      end loop;
   end Parse_Grammars;

   ---------------------------------
   -- Validate_Current_Characters --
   ---------------------------------

   procedure Validate_Current_Characters (Handler : Validating_Reader'Class) is
      Empty_Element : Boolean := False;
   begin
      --  If we were in the middle of a series of Characters callback, we need
      --  to process them now

      if Handler.Validators /= null then
         if Handler.Validators.Characters = null
           and then not Handler.Validators.Is_Nil
         then
            --  No character data => behave as an empty element, but we need to
            --  test explicitely. For instance, the "minLength" facet might or
            --  the "fixed" attribute need to test whether the empty string is
            --  valid.
            Handler.Validators.Characters := new Byte_Sequence'("");
            Empty_Element := True;
         end if;

         if Handler.Validators.Characters /= null then
            if Handler.Validators.Is_Nil then
               if not Empty_Element then
                  Free (Handler.Validators.Characters);
                  Validation_Error
                    ("Element has character data, but is declared as nil");
               end if;

            elsif Has_Fixed (Handler.Validators.Element) then
               if Handler.Validators.Characters.all /=
                 Get_Fixed (Handler.Validators.Element).all
               then
                  Free (Handler.Validators.Characters);
                  Validation_Error
                    ("Element's value must be """
                     & Get_Fixed (Handler.Validators.Element).all & """");
               end if;

            else
               Validate_Characters
                 (Get_Validator (Handler.Validators.Typ),
                  Handler.Validators.Characters.all,
                  Empty_Element => Empty_Element);
            end if;

            Free (Handler.Validators.Characters);
         end if;

         if Handler.Validators.Start_Loc /= null then
            Unref (Handler.Validators.Start_Loc);
         end if;
      end if;
   end Validate_Current_Characters;

   ------------------------
   -- Hook_Start_Element --
   ------------------------

   procedure Hook_Start_Element
     (Handler       : in out Reader'Class;
      Namespace_URI : Unicode.CES.Byte_Sequence := "";
      Local_Name    : Unicode.CES.Byte_Sequence := "";
      Qname         : Unicode.CES.Byte_Sequence := "";
      Atts          : Sax.Attributes.Attributes'Class)
   is
      pragma Unreferenced (Qname);
      Element   : XML_Element := No_Element;
      Data      : Validator_Data;
      Typ       : XML_Type;
      Is_Nil    : Boolean;

      procedure Get_Grammar_From_Attributes;
      --  Parse the grammar, reading its name from the attributes

      procedure Compute_Type;
      --  Compute the type to use, depending on whether the xsi:type attribute
      --  was specified

      ---------------------------------
      -- Get_Grammar_From_Attributes --
      ---------------------------------

      procedure Get_Grammar_From_Attributes is
         No_Index : constant Integer := Get_Index
           (Atts, URI => XML_Instance_URI,
            Local_Name => "noNamespaceSchemaLocation");
         Location_Index : constant Integer := Get_Index
           (Atts, URI => XML_Instance_URI,
            Local_Name => "schemaLocation");
      begin
         if No_Index /= -1 then
            Parse_Grammar (Validating_Reader (Handler),
                           Get_Value (Atts, No_Index),
                           Add_To => Validating_Reader (Handler).Grammar);
         end if;

         if Location_Index /= -1 then
            Parse_Grammars (Validating_Reader (Handler),
                            Get_Value (Atts, Location_Index));
         end if;
      end Get_Grammar_From_Attributes;

      ------------------
      -- Compute_Type --
      ------------------

      procedure Compute_Type is
         Type_Index : constant Integer := Get_Index
           (Atts, URI => XML_Instance_URI, Local_Name => "type");
         G : XML_Grammar_NS;
         Had_Restriction, Had_Extension : Boolean := False;
      begin
         Typ := Get_Type (Element);

         if Type_Index /= -1 then
            if Debug then
               Put_Line ("Getting element definition from type attribute: "
                         & Get_Value (Atts, Type_Index));
            end if;

            declare
               Qname : constant Byte_Sequence := Get_Value (Atts, Type_Index);
               Separator : constant Integer := Split_Qname (Qname);
               Namespace : Byte_Sequence_Access;
            begin
               Namespace := Get_Namespace_From_Prefix
                 (Validating_Reader (Handler),
                  Qname (Qname'First .. Separator - 1));
               if Namespace /= null then
                  Get_NS
                    (Validating_Reader (Handler).Grammar, Namespace.all, G);
               else
                  Get_NS
                    (Validating_Reader (Handler).Grammar, Namespace_URI, G);
               end if;

               Typ := Lookup (G, Qname (Separator + 1 .. Qname'Last),
                              Create_If_Needed => False);
            end;

            if Typ = No_Type then
               Validation_Error
                 ("Unknown type """ & Get_Value (Atts, Type_Index) & '"');
            end if;

            if Get_Validator (Typ) /= Get_Validator (Get_Type (Element)) then
               Check_Replacement
                 (Get_Validator (Typ), Get_Type (Element),
                  Had_Restriction => Had_Restriction,
                  Had_Extension   => Had_Extension);

               if Had_Restriction
                 and then Get_Block_On_Restriction (Element)
               then
                  Validation_Error
                    ("Element """ & Get_Local_Name (Element)
                       & """ blocks the use of restrictions of the type");
               end if;

               if Had_Extension
                 and then Get_Block_On_Extension (Element)
               then
                  Validation_Error
                    ("Element """ & Get_Local_Name (Element)
                       & """ blocks the use of extensions of the type");
               end if;
            end if;
         end if;
      end Compute_Type;

      G : XML_Grammar_NS;
   begin
      if Debug then
         Put_Line (ASCII.ESC & "[33m"
                   & "Start_Element: " & Namespace_URI & ':' & Local_Name
                   & ASCII.ESC & "[39m");
      end if;

      Validate_Current_Characters (Validating_Reader (Handler));

      --  Get the name of the grammar to use from the element's attributes

      if Validating_Reader (Handler).Grammar = No_Grammar then
         Get_Grammar_From_Attributes;

         if Validating_Reader (Handler).Grammar = No_Grammar then
            return;  --  Always valid
         end if;
      end if;

      --  Find out the namespace to use for the current element. This namespace
      --  can be guessed from the parent's element for validation purposes if
      --  we have an unqualified item. Whether qualification should be
      --  mandatory or not is tested later on

      if Namespace_URI = ""
        and then Validating_Reader (Handler).Validators /= null
      then
         G := Validating_Reader (Handler).Validators.Grammar;
      else
         Get_NS (Validating_Reader (Handler).Grammar,
                 Namespace_URI, Result => G);
      end if;

      --  Whether this element is valid in the current context

      if Validating_Reader (Handler).Validators /= null then
         Validate_Start_Element
           (Get_Validator (Validating_Reader (Handler).Validators.Typ),
            Local_Name, Namespace_URI, G,
            Validating_Reader (Handler).Validators.Data,
            Get_Target_NS (Validating_Reader (Handler).Grammar), Element);
      else
         if Debug then
            Put_Line ("Getting element definition from grammar: "
                      & Namespace_URI & " " & Local_Name);
         end if;
         Element := Lookup_Element (G, Local_Name, False);

         if Element = No_Element then
            Validation_Error
              ("Element """ & To_QName (Namespace_URI, Local_Name)
               & """: No matching declaration available");
         end if;

         Add_XML_Instance_Attributes
           (Validating_Reader (Handler), Get_Validator (Get_Type (Element)));
      end if;

      --  If not: this is a validation error

      if Element = No_Element then
         Validation_Error
           ("Unexpected element """ &
            To_QName (Namespace_URI, Local_Name) & """");
      end if;

      Compute_Type;
      Data := Create_Validator_Data (Get_Validator (Typ));

      Validate_Attributes
        (Get_Validator (Typ), Atts, Validating_Reader (Handler).Ids,
         Is_Nillable (Element), Is_Nil,
         Validating_Reader (Handler).Grammar);

      if Validating_Reader (Handler).Validators /= null
        and then Validating_Reader (Handler).Validators.Is_Nil
      then
         Validation_Error
           ("Element is set as nil, and doesn't accept any child element");
      end if;

      Push (Validating_Reader (Handler).Validators, Element,
            Typ, G, Data, Is_Nil);
   end Hook_Start_Element;

   ----------------------
   -- Hook_End_Element --
   ----------------------

   procedure Hook_End_Element
     (Handler       : in out Reader'Class;
      Namespace_URI : Unicode.CES.Byte_Sequence := "";
      Local_Name    : Unicode.CES.Byte_Sequence := "";
      Qname         : Unicode.CES.Byte_Sequence := "")
   is
      pragma Unreferenced (Namespace_URI);
   begin
      if Debug then
         Put_Line (ASCII.ESC & "[33m"
                   & "End_Element: " & Local_Name
                   & ASCII.ESC & "[39m");
      end if;

      if Validating_Reader (Handler).Validators /= null then
         Validate_Current_Characters (Validating_Reader (Handler));

         --  Do not check if the element is nil, since no child is expected
         --  anyway, and some validators (sequence,...) will complain
         if not Validating_Reader (Handler).Validators.Is_Nil then
            Validate_End_Element
              (Get_Validator (Validating_Reader (Handler).Validators.Typ),
               Qname,
               Validating_Reader (Handler).Validators.Data);
         end if;
      end if;
      Pop (Validating_Reader (Handler).Validators);
   end Hook_End_Element;

   -------------------------
   -- Internal_Characters --
   -------------------------

   procedure Internal_Characters
     (Handler : in out Validating_Reader;
      Ch      : Unicode.CES.Byte_Sequence)
   is
      Tmp : Byte_Sequence_Access;
   begin
      if Handler.Validators /= null then
         if Debug then
            Put_Line ("Appending characters --" & Ch & "--");
         end if;
         if Handler.Validators.Characters = null then
            Handler.Validators.Characters := new String'(Ch);
            Handler.Validators.Start_Loc := new Locator_Impl;
            Copy (Handler.Validators.Start_Loc.all, Handler.Locator.all);
         else
            Tmp := new String'(Handler.Validators.Characters.all & Ch);
            Free (Handler.Validators.Characters);
            Handler.Validators.Characters := Tmp;
         end if;
      end if;
   end Internal_Characters;

   ---------------------
   -- Hook_Characters --
   ---------------------

   procedure Hook_Characters
     (Handler : in out Reader'Class;
      Ch      : Unicode.CES.Byte_Sequence) is
   begin
      Internal_Characters (Validating_Reader (Handler), Ch);
   end Hook_Characters;

   -------------------------------
   -- Hook_Ignorable_Whitespace --
   -------------------------------

   procedure Hook_Ignorable_Whitespace
     (Handler : in out Reader'Class;
      Ch      : Unicode.CES.Byte_Sequence) is
   begin
      if Validating_Reader (Handler).Validators /= null
        and then Is_Simple_Type
          (Get_Type (Validating_Reader (Handler).Validators.Element))
        and then not Validating_Reader (Handler).Validators.Is_Nil
      then
         Internal_Characters (Validating_Reader (Handler), Ch);
      end if;
   end Hook_Ignorable_Whitespace;

   -----------
   -- Parse --
   -----------

   procedure Parse
     (Parser : in out Validating_Reader;
      Input  : in out Input_Sources.Input_Source'Class)
   is
      Loc : aliased Locator_Impl;
   begin
      if Get_Feature (Parser, Schema_Validation_Feature) then
         Set_Hooks (Parser,
                    Start_Element => Hook_Start_Element'Access,
                    End_Element   => Hook_End_Element'Access,
                    Characters    => Hook_Characters'Access,
                    Whitespace    => Hook_Ignorable_Whitespace'Access,
                    Start_Prefix  => Hook_Start_Prefix_Mapping'Access,
                    End_Prefix    => Hook_End_Prefix_Mapping'Access,
                    Doc_Locator   => Hook_Set_Document_Locator'Access);
      else
         Set_Hooks (Parser,
                    Start_Element => null,
                    End_Element   => null,
                    Characters    => null,
                    Whitespace    => null,
                    Start_Prefix  => null,
                    End_Prefix    => null,
                    Doc_Locator   => null);
      end if;

      Sax.Readers.Parse (Sax.Readers.Reader (Parser), Input);

      if Parser.Locator /= null then
         Unref (Parser.Locator.all);
      end if;

   exception
      when E : XML_Validation_Error =>
         if Parser.Validators /= null
           and then Parser.Validators.Start_Loc /= null
         then
            Copy (Loc, Parser.Validators.Start_Loc.all);
         else
            Copy (Loc, Parser.Locator.all);
         end if;
         Validation_Error
           (Parser, Create (Byte_Sequence (Exception_Message (E)),
                            Loc'Unchecked_Access));
         Unref (Parser.Locator.all);
   end Parse;

   ----------------------
   -- Validation_Error --
   ----------------------

   procedure Validation_Error
     (Reader : in out Validating_Reader;
      Except  : Sax.Exceptions.Sax_Parse_Exception'Class)
   is
      pragma Unreferenced (Reader);
   begin
      Validation_Error
        (To_String (Get_Locator (Except)) & ": "
         & String (Get_Message (Except)));
   end Validation_Error;

   -------------------------------
   -- Hook_Set_Document_Locator --
   -------------------------------

   procedure Hook_Set_Document_Locator
     (Handler : in out Reader'Class;
      Loc     : access Sax.Locators.Locator'Class) is
   begin
      Validating_Reader (Handler).Locator := Locator_Access (Loc);
      Ref (Validating_Reader (Handler).Locator.all);
   end Hook_Set_Document_Locator;

   -------------------------------
   -- Hook_Start_Prefix_Mapping --
   -------------------------------

   procedure Hook_Start_Prefix_Mapping
     (Handler : in out Reader'Class;
      Prefix  : Unicode.CES.Byte_Sequence;
      URI     : Unicode.CES.Byte_Sequence) is
   begin
      Validating_Reader (Handler).Prefixes := new Prefix_Mapping'
        (Prefix    => new Byte_Sequence'(Prefix),
         Namespace => new Byte_Sequence'(URI),
         Next      => Validating_Reader (Handler).Prefixes);
   end Hook_Start_Prefix_Mapping;

   -----------------------------
   -- Hook_End_Prefix_Mapping --
   -----------------------------

   procedure Hook_End_Prefix_Mapping
     (Handler : in out Reader'Class;
      Prefix  : Unicode.CES.Byte_Sequence)
   is
      Tmp  : Prefix_Mapping_Access := Validating_Reader (Handler).Prefixes;
      Tmp2 : Prefix_Mapping_Access;
   begin
      if Validating_Reader (Handler).Prefixes /= null then
         if Validating_Reader (Handler).Prefixes.Prefix.all = Prefix then
            Validating_Reader (Handler).Prefixes :=
              Validating_Reader (Handler).Prefixes.Next;
            Free (Tmp);
         else
            while Tmp.Next /= null
              and then Tmp.Next.Prefix.all /= Prefix
            loop
               Tmp := Tmp.Next;
            end loop;

            if Tmp.Next /= null then
               Tmp2 := Tmp.Next;
               Tmp.Next := Tmp2.Next;
               Free (Tmp2);
            end if;
         end if;
      end if;
   end Hook_End_Prefix_Mapping;

   -------------------------------
   -- Get_Namespace_From_Prefix --
   -------------------------------

   function Get_Namespace_From_Prefix
     (Handler  : Validating_Reader;
      Prefix   : Unicode.CES.Byte_Sequence)
      return Unicode.CES.Byte_Sequence_Access
   is
      Tmp : Prefix_Mapping_Access := Handler.Prefixes;
   begin
      while Tmp /= null and then Tmp.Prefix.all /= Prefix loop
         Tmp := Tmp.Next;
      end loop;

      if Tmp = null then
         return null;
      else
         return Tmp.Namespace;
      end if;
   end Get_Namespace_From_Prefix;

end Schema.Readers;
