mirror of
https://github.com/AdaCore/xmlada.git
synced 2026-02-12 12:30:28 -08:00
1210 lines
41 KiB
Ada
1210 lines
41 KiB
Ada
------------------------------------------------------------------------------
|
|
-- XML/Ada - An XML suite for Ada95 --
|
|
-- --
|
|
-- Copyright (C) 2003-2017, AdaCore --
|
|
-- --
|
|
-- This library is free software; you can redistribute it and/or modify it --
|
|
-- under terms of the GNU General Public License as published by the Free --
|
|
-- Software Foundation; either version 3, or (at your option) any later --
|
|
-- version. This library is distributed in the hope that it will be useful, --
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
|
|
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
|
|
-- --
|
|
-- As a special exception under Section 7 of GPL version 3, you are granted --
|
|
-- additional permissions described in the GCC Runtime Library Exception, --
|
|
-- version 3.1, as published by the Free Software Foundation. --
|
|
-- --
|
|
-- You should have received a copy of the GNU General Public License and --
|
|
-- a copy of the GCC Runtime Library Exception along with this program; --
|
|
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
|
|
-- <http://www.gnu.org/licenses/>. --
|
|
-- --
|
|
------------------------------------------------------------------------------
|
|
|
|
pragma Ada_05;
|
|
|
|
with Unicode; use Unicode;
|
|
with Unicode.CES; use Unicode.CES;
|
|
with Sax.Locators; use Sax.Locators;
|
|
with Sax.Utils; use Sax.Utils;
|
|
with Sax.Readers; use Sax.Readers;
|
|
with Sax.Symbols; use Sax.Symbols;
|
|
with Schema.Simple_Types; use Schema.Simple_Types;
|
|
with Schema.Validators; use Schema.Validators;
|
|
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
|
|
with Ada.Unchecked_Deallocation;
|
|
with Schema.Schema_Readers; use Schema.Schema_Readers;
|
|
with GNAT.Directory_Operations; use GNAT.Directory_Operations;
|
|
with GNAT.OS_Lib; use GNAT.OS_Lib;
|
|
|
|
package body Schema.Readers is
|
|
use Schema_State_Machines;
|
|
use Schema_State_Machines_Matchers;
|
|
|
|
procedure Print is new Schema_State_Machines_Matchers.Debug_Print
|
|
(Schema_State_Machines_PP.Node_Label);
|
|
|
|
procedure Internal_Characters
|
|
(Handler : access Validating_Reader'Class;
|
|
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 : access Validating_Reader'Class;
|
|
Loc : Location);
|
|
-- Validate the current set of characters
|
|
|
|
procedure Reset (Parser : in out Validating_Reader);
|
|
-- Reset the state of the parser so that we can parse other documents.
|
|
-- This doesn't reset the grammar
|
|
|
|
procedure Hook_Start_Element
|
|
(Handler : access Sax_Reader'Class;
|
|
Elem : Element_Access;
|
|
Atts : access Sax_Attribute_List);
|
|
procedure Hook_End_Element
|
|
(Handler : access Sax_Reader'Class;
|
|
Elem : Element_Access);
|
|
procedure Hook_Characters
|
|
(Handler : access Sax_Reader'Class;
|
|
Ch : Unicode.CES.Byte_Sequence);
|
|
procedure Hook_Ignorable_Whitespace
|
|
(Handler : access Sax_Reader'Class;
|
|
Ch : Unicode.CES.Byte_Sequence);
|
|
procedure Hook_Notation_Decl
|
|
(Handler : access Sax_Reader'Class;
|
|
Name : Unicode.CES.Byte_Sequence;
|
|
Public_Id : Unicode.CES.Byte_Sequence;
|
|
System_Id : Unicode.CES.Byte_Sequence);
|
|
-- See for the corresponding primitive operations. These provide the
|
|
-- necessary validation hooks.
|
|
|
|
-----------------
|
|
-- Set_Grammar --
|
|
-----------------
|
|
|
|
procedure Set_Grammar
|
|
(Reader : in out Validating_Reader;
|
|
Grammar : Schema.Validators.XML_Grammar)
|
|
is
|
|
use Symbol_Table_Pointers;
|
|
begin
|
|
if Debug then
|
|
Debug_Output ("Set_Grammar");
|
|
end if;
|
|
|
|
if Grammar /= No_Grammar then
|
|
if Get (Get_Symbol_Table (Reader)) = null then
|
|
if Debug then
|
|
Debug_Output ("Set reader's symbol table from grammar");
|
|
end if;
|
|
|
|
Set_Symbol_Table (Reader, Get_Symbol_Table (Grammar));
|
|
|
|
elsif Get_Symbol_Table (Grammar) =
|
|
Symbol_Table_Pointers.Null_Pointer
|
|
then
|
|
if Debug then
|
|
Debug_Output ("Set grammar's symbol table from reader");
|
|
end if;
|
|
Set_Symbol_Table (Grammar, Get_Symbol_Table (Reader));
|
|
|
|
elsif Get_Symbol_Table (Reader) /= Get_Symbol_Table (Grammar) then
|
|
raise XML_Fatal_Error with
|
|
"The grammar and the reader must use the same symbol table";
|
|
end if;
|
|
end if;
|
|
|
|
Reader.Grammar := Grammar;
|
|
end Set_Grammar;
|
|
|
|
----------------------
|
|
-- Set_Symbol_Table --
|
|
----------------------
|
|
|
|
overriding procedure Set_Symbol_Table
|
|
(Parser : in out Validating_Reader;
|
|
Symbols : Sax.Utils.Symbol_Table)
|
|
is
|
|
use Symbol_Table_Pointers;
|
|
begin
|
|
if Parser.Grammar /= No_Grammar
|
|
and then Get_Symbol_Table (Parser.Grammar) /= Symbols
|
|
then
|
|
raise XML_Fatal_Error with
|
|
"The grammar and the reader must use the same symbol table";
|
|
end if;
|
|
|
|
if Symbols /= Get_Symbol_Table (Parser) then
|
|
Parser.Xmlns := No_Symbol; -- Will force another lookup next time
|
|
Set_Symbol_Table (Sax_Reader (Parser), Symbols);
|
|
end if;
|
|
end Set_Symbol_Table;
|
|
|
|
-----------------
|
|
-- Get_Grammar --
|
|
-----------------
|
|
|
|
function Get_Grammar
|
|
(Reader : Validating_Reader) return Schema.Validators.XML_Grammar is
|
|
begin
|
|
return Reader.Grammar;
|
|
end Get_Grammar;
|
|
|
|
---------------------
|
|
-- To_Absolute_URI --
|
|
---------------------
|
|
|
|
function To_Absolute_URI
|
|
(Handler : Validating_Reader;
|
|
URI : Symbol) return Symbol is
|
|
begin
|
|
if URI = Empty_String
|
|
or else Starts_With (Get (URI).all, "http:/")
|
|
then
|
|
return URI;
|
|
else
|
|
-- Need to resolve symbolic links and relative dirs, since parsing
|
|
-- the same XSD several times will result in errors (duplicate ID for
|
|
-- instance)
|
|
return Find_Symbol
|
|
(Handler,
|
|
Normalize_Pathname
|
|
(Name => Get (URI).all,
|
|
Directory =>
|
|
Dir_Name (Get (Handler.Current_Location.System_Id).all),
|
|
Resolve_Links => True));
|
|
end if;
|
|
end To_Absolute_URI;
|
|
|
|
--------------------
|
|
-- Parse_Grammars --
|
|
--------------------
|
|
|
|
procedure Parse_Grammars
|
|
(Handler : access Validating_Reader'Class;
|
|
Schema_Location : Symbol;
|
|
Do_Create_NFA : Boolean)
|
|
is
|
|
URI : Symbol := No_Symbol;
|
|
|
|
procedure Callback (Ch : Byte_Sequence);
|
|
procedure Callback (Ch : Byte_Sequence) is
|
|
begin
|
|
if URI = No_Symbol then
|
|
URI := Find_Symbol (Handler.all, Ch);
|
|
else
|
|
Parse_Grammar
|
|
(Handler,
|
|
URI => URI,
|
|
Xsd_File => Find_Symbol (Handler.all, Ch),
|
|
Do_Create_NFA => Do_Create_NFA);
|
|
URI := No_Symbol;
|
|
end if;
|
|
end Callback;
|
|
|
|
procedure For_Each is new For_Each_Item (Callback);
|
|
|
|
begin
|
|
For_Each (Get (Schema_Location).all);
|
|
end Parse_Grammars;
|
|
|
|
---------------------------------
|
|
-- Validate_Current_Characters --
|
|
---------------------------------
|
|
|
|
procedure Validate_Current_Characters
|
|
(Handler : access Validating_Reader'Class;
|
|
Loc : Location)
|
|
is
|
|
Is_Empty : Boolean;
|
|
Whitespace : Whitespace_Restriction := Preserve;
|
|
NFA : constant Schema_NFA_Access := Get_NFA (Handler.Grammar);
|
|
S : State;
|
|
Descr : access Type_Descr;
|
|
Fixed : Symbol := No_Symbol;
|
|
Default : Symbol := No_Symbol;
|
|
Data : State_Data;
|
|
Ty : Type_Index;
|
|
Is_Equal : Boolean;
|
|
|
|
begin
|
|
if Debug then
|
|
Print (Handler.Matcher, Dump_Compact, "Validate_Current_Char: ");
|
|
end if;
|
|
|
|
-- Handling of nil elements
|
|
|
|
if Handler.Is_Nil then
|
|
if Handler.Characters_Count /= 0 then
|
|
Validation_Error
|
|
(Handler,
|
|
"No character content allowed because the element is 'nilled'",
|
|
Loc);
|
|
end if;
|
|
|
|
return; -- Content is always considered valid
|
|
end if;
|
|
|
|
-- Check all active states to find our whitespace normalization rules,
|
|
-- and whether elements have fixed values. Note that the fixed value
|
|
-- is attached to an state with a nested state (ie the state
|
|
-- representing the element itself).
|
|
|
|
declare
|
|
Iter : Active_State_Iterator :=
|
|
For_Each_Active_State (Handler.Matcher,
|
|
Ignore_If_Nested => True,
|
|
Ignore_If_Default => True);
|
|
begin
|
|
loop
|
|
S := Current (Handler.Matcher, Iter);
|
|
exit when S = No_State;
|
|
|
|
Data := Current_Data (Handler.Matcher, Iter);
|
|
|
|
if Fixed = No_Symbol then
|
|
Fixed := Data.Fixed;
|
|
|
|
-- Get the "fixed" value from the element
|
|
-- (if it has a complexType)
|
|
if Fixed = No_Symbol and then Has_Parent (Iter) then
|
|
Fixed := Current_Data (Handler.Matcher, Parent (Iter)).Fixed;
|
|
end if;
|
|
end if;
|
|
|
|
if Default = No_Symbol then
|
|
Default := Data.Default;
|
|
if Default = No_Symbol and then Has_Parent (Iter) then
|
|
Default :=
|
|
Current_Data (Handler.Matcher, Parent (Iter)).Default;
|
|
end if;
|
|
end if;
|
|
|
|
-- Unless we have a <any> type
|
|
if Data.Simple /= No_Type_Index then
|
|
Descr := Get_Type_Descr (NFA, Data.Simple);
|
|
if Descr.Simple_Content /= No_Simple_Type_Index then
|
|
Whitespace := Get_Simple_Type
|
|
(Get_NFA (Handler.Grammar), Descr.Simple_Content)
|
|
.Whitespace;
|
|
end if;
|
|
end if;
|
|
|
|
Next (Handler.Matcher, Iter);
|
|
end loop;
|
|
end;
|
|
|
|
Is_Empty := Handler.Characters_Count = 0;
|
|
|
|
if not Is_Empty then
|
|
if Debug then
|
|
Debug_Output ("Normalize whitespace: " & Whitespace'Img);
|
|
end if;
|
|
|
|
Normalize_Whitespace
|
|
(Whitespace, Handler.Characters.all, Handler.Characters_Count);
|
|
end if;
|
|
|
|
-- in 3.3.1: if the element is empty, the "fixed" value
|
|
-- should be used for it, just as for "default"
|
|
-- Characters (Handler.all, Get (Get_Fixed (Handler)).all);
|
|
|
|
if Is_Empty and then Fixed /= No_Symbol then
|
|
Internal_Characters (Handler, Get (Fixed).all);
|
|
Is_Empty := Handler.Characters_Count = 0;
|
|
if Debug then
|
|
Debug_Output
|
|
("Substitute fixed value for empty characters:"
|
|
& Get (Fixed).all);
|
|
end if;
|
|
end if;
|
|
|
|
-- If still empty, use the default value
|
|
|
|
if Is_Empty and then Default /= No_Symbol then
|
|
Internal_Characters (Handler, Get (Default).all);
|
|
Is_Empty := Handler.Characters_Count = 0;
|
|
if Debug then
|
|
Debug_Output
|
|
("Substitute default value for empty characters:"
|
|
& Get (Default).all);
|
|
end if;
|
|
end if;
|
|
|
|
declare
|
|
Iter : Active_State_Iterator :=
|
|
For_Each_Active_State (Handler.Matcher,
|
|
Ignore_If_Nested => True,
|
|
Ignore_If_Default => True);
|
|
begin
|
|
loop
|
|
S := Current (Handler.Matcher, Iter);
|
|
exit when S = No_State;
|
|
|
|
Ty := Current_Data (Handler.Matcher, Iter).Simple;
|
|
|
|
if Ty /= No_Type_Index then
|
|
Descr := Get_Type_Descr (NFA, Ty);
|
|
|
|
if Descr.Simple_Content /= No_Simple_Type_Index then
|
|
if Debug and not Is_Empty then
|
|
Debug_Output
|
|
("Validate characters ("
|
|
& To_QName (Descr.Name) & "): "
|
|
& Handler.Characters (1 .. Handler.Characters_Count)
|
|
& "--");
|
|
end if;
|
|
|
|
if Handler.Characters_Count = 0 then
|
|
Validate_Simple_Type
|
|
(Handler, Descr.Simple_Content,
|
|
"",
|
|
Loc => Loc);
|
|
else
|
|
Validate_Simple_Type
|
|
(Handler, Descr.Simple_Content,
|
|
Handler.Characters (1 .. Handler.Characters_Count),
|
|
Loc => Loc);
|
|
end if;
|
|
|
|
elsif not Descr.Mixed
|
|
and then not Is_Empty
|
|
then
|
|
if Debug then
|
|
Debug_Output ("No character data for "
|
|
& To_QName (Descr.Name) & S'Img);
|
|
Debug_Output
|
|
("Got "
|
|
& Handler.Characters
|
|
(1 .. Integer'Min (20, Handler.Characters_Count))
|
|
& "--");
|
|
end if;
|
|
Validation_Error
|
|
(Handler,
|
|
"No character data allowed by content model",
|
|
Loc);
|
|
end if;
|
|
|
|
-- We now know we have a valid character content, and we need
|
|
-- to check it is equal to the fixed value. We also know that
|
|
-- fixed matches the type, since it was checked when the XSD
|
|
-- was parsed.
|
|
|
|
if Fixed /= No_Symbol then
|
|
if Debug then
|
|
Debug_Output ("Element has fixed value: """
|
|
& Get (Fixed).all & '"');
|
|
end if;
|
|
|
|
if Descr.Simple_Content /= No_Simple_Type_Index then
|
|
Is_Equal := Equal
|
|
(Reader => Handler,
|
|
Simple_Type => Descr.Simple_Content,
|
|
Ch1 => Fixed,
|
|
Ch2 =>
|
|
Handler.Characters (1 .. Handler.Characters_Count));
|
|
else
|
|
Is_Equal := Get (Fixed).all =
|
|
Handler.Characters (1 .. Handler.Characters_Count);
|
|
end if;
|
|
|
|
if not Is_Equal then
|
|
Validation_Error
|
|
(Handler,
|
|
"Invalid character content (fixed to """
|
|
& Get (Fixed).all & """)");
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
Next (Handler.Matcher, Iter);
|
|
end loop;
|
|
end;
|
|
|
|
Handler.Characters_Count := 0;
|
|
end Validate_Current_Characters;
|
|
|
|
------------------------
|
|
-- Hook_Notation_Decl --
|
|
------------------------
|
|
|
|
procedure Hook_Notation_Decl
|
|
(Handler : access Sax_Reader'Class;
|
|
Name : Unicode.CES.Byte_Sequence;
|
|
Public_Id : Unicode.CES.Byte_Sequence;
|
|
System_Id : Unicode.CES.Byte_Sequence)
|
|
is
|
|
pragma Unreferenced (Public_Id, System_Id);
|
|
H : constant Validating_Reader_Access :=
|
|
Validating_Reader_Access (Handler);
|
|
begin
|
|
Add_Notation (Get_NFA (H.Grammar), Find_Symbol (H.all, Name));
|
|
end Hook_Notation_Decl;
|
|
|
|
------------------------
|
|
-- Hook_Start_Element --
|
|
------------------------
|
|
|
|
procedure Hook_Start_Element
|
|
(Handler : access Sax_Reader'Class;
|
|
Elem : Element_Access;
|
|
Atts : access Sax_Attribute_List)
|
|
is
|
|
H : constant Validating_Reader_Access :=
|
|
Validating_Reader_Access (Handler);
|
|
No_Index : constant Integer := Get_Index
|
|
(Atts.all, H.XML_Instance_URI, H.No_Namespace_Schema_Location);
|
|
Location_Index : constant Integer := Get_Index
|
|
(Atts.all, H.XML_Instance_URI, H.Schema_Location);
|
|
NFA : Schema_NFA_Access;
|
|
|
|
procedure Compute_Type_From_Attribute
|
|
(Result_Index : out Type_Index;
|
|
Result : out Type_Descr_Access);
|
|
-- If xsi:type was specified, verify that the given type is a valid
|
|
-- substitution for the original type in the NFA, and replace the
|
|
-- current nested automaton with the one for the type. The replacement
|
|
-- does not affect the NFA itself, but the NFA_Matcher, so is only
|
|
-- temporary and does not affect over running matchers.
|
|
--
|
|
-- Return the first state in the nested NFA to represent that type
|
|
|
|
procedure Replace_State
|
|
(Check_Substitution : Boolean;
|
|
Nested_Start : State;
|
|
Simple : Type_Index);
|
|
-- Replace the current most nested NFA with [Nested_Start], to override
|
|
-- the type. This might mean replacing a nested NFA or a state data,
|
|
-- depending on whether we have a simpleType or complexType
|
|
|
|
function Simple_Type_Data
|
|
(Iter : Active_State_Iterator) return State_Data;
|
|
-- return the simpleType data for the current state. This is either
|
|
-- queries from the current state itself, or from its superstate if
|
|
-- we are currently on the first state of the nested NFA.
|
|
|
|
----------------------
|
|
-- Simple_Type_Data --
|
|
----------------------
|
|
|
|
function Simple_Type_Data
|
|
(Iter : Active_State_Iterator) return State_Data
|
|
is
|
|
S : constant State := Current (H.Matcher, Iter);
|
|
begin
|
|
if Has_Parent (Iter)
|
|
and then Get_Start_State
|
|
(NFA.Get_Nested (Current (H.Matcher, Parent (Iter)))) = S
|
|
then
|
|
return Current_Data (H.Matcher, Parent (Iter));
|
|
else
|
|
return Current_Data (H.Matcher, Iter);
|
|
end if;
|
|
end Simple_Type_Data;
|
|
|
|
-------------------
|
|
-- Replace_State --
|
|
-------------------
|
|
|
|
procedure Replace_State
|
|
(Check_Substitution : Boolean;
|
|
Nested_Start : State;
|
|
Simple : Type_Index)
|
|
is
|
|
S : State := No_State;
|
|
Data : State_Data;
|
|
Iter : Active_State_Iterator :=
|
|
For_Each_Active_State
|
|
(H.Matcher, Ignore_If_Default => True, Ignore_If_Nested => True);
|
|
Internal_New_Nested : State := Nested_Start;
|
|
begin
|
|
loop
|
|
S := Current (H.Matcher, Iter);
|
|
exit when S = No_State;
|
|
|
|
Data := Current_Data (H.Matcher, Iter);
|
|
|
|
if Check_Substitution then
|
|
Check_Substitution_Group_OK
|
|
(H, Simple,
|
|
Data.Simple,
|
|
Loc => H.Current_Location,
|
|
Element_Block => Simple_Type_Data (Iter).Block);
|
|
end if;
|
|
|
|
if Nested_Start = No_State then
|
|
-- Need to modify the nested NFA too: if we replaced a
|
|
-- complexType ("anyType" for instance) with a simple type,
|
|
-- we should no longer accept any element.
|
|
-- However, if we simply disable all states in the nested
|
|
-- NFA, that doesn't work either, since we will not accept
|
|
-- the "close element" for the simpleType. But we cannot
|
|
-- modify the NFA either, which should remain static.
|
|
|
|
if Debug then
|
|
Debug_Output ("Override state data"
|
|
& Current (H.Matcher, Iter)'Img
|
|
& " to type" & Simple'Img);
|
|
end if;
|
|
Override_Data
|
|
(H.Matcher, Iter,
|
|
State_Data'
|
|
(Simple => Simple,
|
|
Nillable => Data.Nillable,
|
|
Fixed => Data.Fixed,
|
|
Default => Data.Default,
|
|
Block => Data.Block));
|
|
|
|
Internal_New_Nested := NFA.Simple_Nested;
|
|
if Debug then
|
|
Debug_Output
|
|
("Will replace nested complexType, to accept <close>");
|
|
end if;
|
|
end if;
|
|
|
|
if Internal_New_Nested /= No_State then
|
|
-- If we are on the first state of the parent, that means
|
|
-- we just entered the parent (which is the element having
|
|
-- the xsi:type). So we substitute the nested NFA *for the
|
|
-- parent*.
|
|
|
|
if Has_Parent (Iter) then
|
|
if Get_Start_State
|
|
(NFA.Get_Nested (Current (H.Matcher, Parent (Iter)))) = S
|
|
then
|
|
if Debug then
|
|
Debug_Output ("Replacing nested NFA for" & S'Img
|
|
& " with" & Internal_New_Nested'Img);
|
|
end if;
|
|
Replace_State (H.Matcher, Iter, Internal_New_Nested);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
Next (H.Matcher, Iter);
|
|
end loop;
|
|
|
|
if Debug then
|
|
Print (H.Matcher, Dump_Compact, "After substitution:");
|
|
end if;
|
|
end Replace_State;
|
|
|
|
---------------------------------
|
|
-- Compute_Type_From_Attribute --
|
|
---------------------------------
|
|
|
|
procedure Compute_Type_From_Attribute
|
|
(Result_Index : out Type_Index;
|
|
Result : out Type_Descr_Access)
|
|
is
|
|
Xsi_Type_Index : constant Integer := Get_Index
|
|
(Atts.all, H.XML_Instance_URI, H.Typ);
|
|
TRef : Global_Reference;
|
|
begin
|
|
if Xsi_Type_Index = -1 then
|
|
Result_Index := No_Type_Index;
|
|
Result := null;
|
|
else
|
|
declare
|
|
Qname : constant Byte_Sequence :=
|
|
Ada.Strings.Fixed.Trim
|
|
(Get (Get_Value (Atts.all, Xsi_Type_Index)).all,
|
|
Ada.Strings.Both);
|
|
Separator : constant Integer := Split_Qname (Qname);
|
|
Prefix : Symbol;
|
|
NS : XML_NS;
|
|
Typ : Qualified_Name;
|
|
begin
|
|
Prefix := Find_Symbol
|
|
(H.all, Qname (Qname'First .. Separator - 1));
|
|
Get_Namespace_From_Prefix (H.all, Prefix, NS);
|
|
|
|
Typ := (NS => Get_URI (NS),
|
|
Local => Find_Symbol
|
|
(H.all, Qname (Separator + 1 .. Qname'Last)));
|
|
|
|
if Debug then
|
|
Debug_Output
|
|
("Getting element definition from type attribute: "
|
|
& To_QName (Typ));
|
|
end if;
|
|
|
|
TRef := Reference_HTables.Get
|
|
(Get_References (H.Grammar).all, (Typ, Ref_Type));
|
|
|
|
if TRef = No_Global_Reference then
|
|
Validation_Error (H, "Unknown type " & To_QName (Typ));
|
|
end if;
|
|
|
|
Result_Index := TRef.Typ;
|
|
Result := Type_Descr_Access (Get_Type_Descr (NFA, TRef.Typ));
|
|
Replace_State
|
|
(Check_Substitution => True,
|
|
Nested_Start => Result.Complex_Content,
|
|
Simple => TRef.Typ);
|
|
end;
|
|
end if;
|
|
end Compute_Type_From_Attribute;
|
|
|
|
Success : Boolean;
|
|
Nil_Index : Integer := -1;
|
|
Nillable : Boolean := False;
|
|
S : State;
|
|
Through_Any : Boolean;
|
|
Through_Process : Process_Contents_Type;
|
|
TRef : Global_Reference;
|
|
Xsi_Descr : Type_Descr_Access;
|
|
Xsi_Index : Type_Index;
|
|
|
|
Had_Matcher : Boolean;
|
|
|
|
Element_QName : constant Qualified_Name :=
|
|
(NS => Get_URI (Get_NS (Elem)),
|
|
Local => Get_Local_Name (Elem));
|
|
|
|
begin
|
|
if Debug then
|
|
Output_Seen ("Start_Element: " & To_QName (Element_QName)
|
|
& " " & To_String (H.Current_Location));
|
|
end if;
|
|
|
|
-- We should get the location of the enclosing element
|
|
|
|
Validate_Current_Characters (H, Loc => Start_Tag_Location (Elem));
|
|
|
|
-- Get the name of the grammar to use from the element's attributes
|
|
|
|
if No_Index /= -1 then
|
|
Parse_Grammar
|
|
(H,
|
|
URI => Empty_String,
|
|
Xsd_File => Get_Value (Atts.all, No_Index),
|
|
Do_Create_NFA => True);
|
|
end if;
|
|
|
|
if Location_Index /= -1 then
|
|
Parse_Grammars
|
|
(H, Get_Value (Atts.all, Location_Index), Do_Create_NFA => True);
|
|
end if;
|
|
|
|
-- We now have a grammar set
|
|
|
|
Had_Matcher := Is_Initialized (H.Matcher);
|
|
NFA := Get_NFA (H.Grammar);
|
|
|
|
-- If we have an inline schema, we must check that the target NS
|
|
-- is not used yet
|
|
|
|
if Element_QName = (NS => H.XML_Schema_URI, Local => H.S_Schema)
|
|
and then Had_Matcher
|
|
then
|
|
-- ??? Would need to include the contents into the NFA
|
|
-- ??? And check that no element from the same namespace was seen
|
|
Validation_Error
|
|
(H,
|
|
"Inline schema not supported",
|
|
Except => XML_Not_Implemented'Identity);
|
|
end if;
|
|
|
|
if H.Grammar = No_Grammar then
|
|
return; -- Always valid, since we have no grammar anyway
|
|
end if;
|
|
|
|
-- Create the NFA matcher now if not done yet. This has to be done after
|
|
-- we have seen the toplevel element, which might result in parsing
|
|
-- additional grammars, and finding the target NS
|
|
|
|
if not Had_Matcher then
|
|
if Debug then
|
|
Debug_Output ("Creating NFA matcher");
|
|
end if;
|
|
|
|
H.Matcher.Start_Match
|
|
(On => Get_NFA (H.Grammar),
|
|
Start_At => Start_State);
|
|
end if;
|
|
|
|
Do_Match
|
|
(Matcher => H.Matcher,
|
|
Sym => (Closing => False, Name => Element_QName),
|
|
Success => Success,
|
|
Through_Any => Through_Any,
|
|
Through_Process => Through_Process);
|
|
|
|
if Debug then
|
|
Print (H.Matcher, Dump_Compact, "After: ");
|
|
end if;
|
|
|
|
if not Had_Matcher and not Success then
|
|
-- Seeing the toplevel is never incorrect. We just need to find
|
|
-- out what its type would be, and use this for the matcher
|
|
|
|
declare
|
|
Descr : Type_Descr_Access;
|
|
Index : Type_Index;
|
|
begin
|
|
Compute_Type_From_Attribute (Index, Descr);
|
|
if Descr = null then
|
|
Validation_Error
|
|
(H, "No type found for " & To_QName (Element_QName));
|
|
elsif Descr.Complex_Content /= No_State then
|
|
H.Matcher.Start_Match
|
|
(On => Get_NFA (H.Grammar),
|
|
Start_At => Descr.Complex_Content);
|
|
else
|
|
-- Just expect a "close". The current active state, however,
|
|
-- ends up with no state data, and we need to set it to the
|
|
-- appropriate simpleType. Can't use Replace_State for this.
|
|
|
|
H.Matcher.Start_Match
|
|
(Get_NFA (H.Grammar),
|
|
Start_At => NFA.Simple_Nested);
|
|
|
|
declare
|
|
Iter : constant Active_State_Iterator :=
|
|
For_Each_Active_State
|
|
(H.Matcher, Ignore_If_Default => False,
|
|
Ignore_If_Nested => True);
|
|
Data : State_Data;
|
|
begin
|
|
Data := Current_Data (H.Matcher, Iter);
|
|
Override_Data
|
|
(H.Matcher, Iter,
|
|
State_Data'
|
|
(Simple => Index,
|
|
Nillable => Data.Nillable,
|
|
Fixed => Data.Fixed,
|
|
Default => Data.Default,
|
|
Block => Data.Block));
|
|
end;
|
|
end if;
|
|
end;
|
|
|
|
elsif not Success then
|
|
Validation_Error
|
|
(H, "Unexpected element """
|
|
& To_QName (Element_QName) & """: expecting """
|
|
& Expected (H.Matcher) & '"');
|
|
end if;
|
|
|
|
-- If we have a xsi:type attribute, modify the NFA to use that type
|
|
|
|
Compute_Type_From_Attribute (Xsi_Index, Xsi_Descr);
|
|
|
|
-- If the element matched a <any>, we might have to look it up to get
|
|
-- its type. However, if a type was given through xsi:type, this is
|
|
-- not needed since we already have a type.
|
|
|
|
if Through_Any and then Xsi_Descr = null then
|
|
case Through_Process is
|
|
when Process_Skip =>
|
|
-- Need to lookup the element to see whether it is nillable.
|
|
-- Apparently, this aspect must be checked.
|
|
-- Apart from that, this case is already handled in the NFA,
|
|
-- and the state is setup as ur-Type
|
|
TRef := Reference_HTables.Get
|
|
(Get_References (H.Grammar).all,
|
|
(Element_QName, Ref_Element));
|
|
|
|
if TRef /= No_Global_Reference then
|
|
Nillable := NFA.Get_Data (TRef.Element).Nillable;
|
|
if Debug then
|
|
Debug_Output ("Getting nillable status from schema"
|
|
& " even though we are in a <any skip>");
|
|
end if;
|
|
end if;
|
|
|
|
when Process_Lax =>
|
|
TRef := Reference_HTables.Get
|
|
(Get_References (H.Grammar).all,
|
|
(Element_QName, Ref_Element));
|
|
|
|
when Process_Strict =>
|
|
-- Find the definition for this element, if possible
|
|
|
|
TRef := Reference_HTables.Get
|
|
(Get_References (H.Grammar).all,
|
|
(Element_QName, Ref_Element));
|
|
|
|
if TRef = No_Global_Reference then
|
|
Validation_Error
|
|
(H, "No definition found for "
|
|
& To_QName (Element_QName));
|
|
end if;
|
|
end case;
|
|
|
|
if Through_Process /= Process_Skip
|
|
and then TRef /= No_Global_Reference
|
|
then
|
|
-- Replace the current most nested state in the machine with the
|
|
-- new type
|
|
|
|
if Debug then
|
|
Debug_Output ("Found valid declaration for "
|
|
& To_QName (Element_QName));
|
|
end if;
|
|
|
|
Replace_State
|
|
(Check_Substitution => False,
|
|
Nested_Start => Get_Start_State (NFA.Get_Nested (TRef.Element)),
|
|
Simple => NFA.Get_Data (TRef.Element).Simple);
|
|
end if;
|
|
|
|
else
|
|
Through_Process := Process_Strict;
|
|
end if;
|
|
|
|
-- Validate the attributes
|
|
|
|
declare
|
|
Iter : Active_State_Iterator :=
|
|
For_Each_Active_State
|
|
(H.Matcher, Ignore_If_Nested => True, Ignore_If_Default => True);
|
|
Data : State_Data;
|
|
Fixed : Symbol := No_Symbol;
|
|
begin
|
|
loop
|
|
S := Current (H.Matcher, Iter);
|
|
exit when S = No_State;
|
|
|
|
-- The list of valid attributes is attached to the type, that is
|
|
-- to the nested NFA.
|
|
|
|
Data := Simple_Type_Data (Iter);
|
|
|
|
if Fixed = No_Symbol then
|
|
Fixed := Data.Fixed;
|
|
end if;
|
|
|
|
if Debug then
|
|
Debug_Output ("Checking attributes for state" & S'Img
|
|
& " type_index=" & Data.Simple'Img);
|
|
end if;
|
|
|
|
Nillable := Nillable or Data.Nillable;
|
|
|
|
-- otherwise with have a <any> type
|
|
if Data.Simple /= No_Type_Index then
|
|
|
|
-- Check whether the actual type is abstract. This cannot be
|
|
-- checked when the grammar is created because of
|
|
-- substitutionGroup and xsi:type
|
|
|
|
declare
|
|
Descr : constant access Type_Descr :=
|
|
NFA.Get_Type_Descr (Data.Simple);
|
|
begin
|
|
if Descr.Is_Abstract then
|
|
if Descr.Name /= No_Qualified_Name then
|
|
Validation_Error
|
|
(H,
|
|
"Type " & To_QName (Descr.Name) & " is abstract");
|
|
else
|
|
Validation_Error (H, "Type is abstract");
|
|
end if;
|
|
end if;
|
|
|
|
Validate_Attributes
|
|
(Get_NFA (H.Grammar),
|
|
Descr,
|
|
H, Atts,
|
|
Is_Nil => Nil_Index);
|
|
end;
|
|
else
|
|
if Debug then
|
|
Debug_Output ("A <anyType>, all attributes are valid");
|
|
end if;
|
|
|
|
Nil_Index :=
|
|
Get_Index (Atts.all, H.XML_Instance_URI, H.Nil);
|
|
end if;
|
|
|
|
Next (H.Matcher, Iter);
|
|
end loop;
|
|
|
|
if Through_Process = Process_Skip then
|
|
-- In this case, we do not want to check the contents. Even if
|
|
-- xsi:nil="true" was specified, we still need to accept when
|
|
-- contents was provided.
|
|
H.Is_Nil := False;
|
|
|
|
else
|
|
if Nil_Index /= -1 then
|
|
if not Nillable then
|
|
Validation_Error (H, "Element cannot be nil");
|
|
end if;
|
|
H.Is_Nil := Get_Value_As_Boolean (Atts.all, Nil_Index);
|
|
else
|
|
H.Is_Nil := False;
|
|
end if;
|
|
|
|
if H.Is_Nil then
|
|
if Fixed /= No_Symbol then
|
|
Validation_Error
|
|
(H, "Element cannot be nilled because"
|
|
& " a fixed value is defined for it");
|
|
end if;
|
|
|
|
if Debug then
|
|
Debug_Output
|
|
("Element is nil, should we replace nested NFA");
|
|
end if;
|
|
|
|
Replace_State
|
|
(Check_Substitution => False,
|
|
Nested_Start => NFA.Simple_Nested,
|
|
Simple => Data.Simple);
|
|
end if;
|
|
end if;
|
|
end;
|
|
end Hook_Start_Element;
|
|
|
|
----------------------
|
|
-- Hook_End_Element --
|
|
----------------------
|
|
|
|
procedure Hook_End_Element
|
|
(Handler : access Sax_Reader'Class;
|
|
Elem : Element_Access)
|
|
is
|
|
H : constant Validating_Reader_Access :=
|
|
Validating_Reader_Access (Handler);
|
|
Success : Boolean;
|
|
Through_Any : Boolean;
|
|
Through_Process : Process_Contents_Type;
|
|
begin
|
|
if Debug then
|
|
Output_Seen
|
|
("End_Element: "
|
|
& To_QName (Elem) & " " & To_String (H.Current_Location));
|
|
end if;
|
|
|
|
Validate_Current_Characters (H, Loc => Start_Tag_End_Location (Elem));
|
|
|
|
Do_Match
|
|
(H.Matcher,
|
|
Sym => (Closing => True,
|
|
Name => (NS => Get_URI (Get_NS (Elem)),
|
|
Local => Get_Local_Name (Elem))),
|
|
Success => Success,
|
|
Through_Any => Through_Any,
|
|
Through_Process => Through_Process);
|
|
|
|
if Debug then
|
|
Print (H.Matcher, Dump_Compact, "After end element: ");
|
|
end if;
|
|
|
|
if not Success then
|
|
Validation_Error
|
|
(H,
|
|
"Unexpected end of sequence, expecting """
|
|
& Expected (H.Matcher) & '"');
|
|
end if;
|
|
|
|
-- We know the parent wasn't nil, since the child was accepted
|
|
H.Is_Nil := False;
|
|
end Hook_End_Element;
|
|
|
|
-------------------------
|
|
-- Internal_Characters --
|
|
-------------------------
|
|
|
|
procedure Internal_Characters
|
|
(Handler : access Validating_Reader'Class;
|
|
Ch : Unicode.CES.Byte_Sequence)
|
|
is
|
|
Tmp : Byte_Sequence_Access;
|
|
Max : constant Natural := Handler.Characters_Count + Ch'Length;
|
|
begin
|
|
-- Preserve the characters, but avoid allocating every time. We
|
|
-- therefore reuse the buffer as much as possible, and only extend it
|
|
-- when needed.
|
|
|
|
if Handler.Characters = null then
|
|
Handler.Characters_Count := Ch'Length;
|
|
Handler.Characters := new String (1 .. Ch'Length);
|
|
Handler.Characters.all := Ch;
|
|
|
|
elsif Max <= Handler.Characters'Last then
|
|
Handler.Characters (Handler.Characters_Count + 1 .. Max) := Ch;
|
|
Handler.Characters_Count := Max;
|
|
|
|
else
|
|
Tmp := new String (1 .. Max);
|
|
Tmp (1 .. Handler.Characters_Count) :=
|
|
Handler.Characters (1 .. Handler.Characters_Count);
|
|
Tmp (Handler.Characters_Count + 1 .. Max) := Ch;
|
|
Handler.Characters_Count := Max;
|
|
Free (Handler.Characters);
|
|
Handler.Characters := Tmp;
|
|
end if;
|
|
end Internal_Characters;
|
|
|
|
---------------------
|
|
-- Hook_Characters --
|
|
---------------------
|
|
|
|
procedure Hook_Characters
|
|
(Handler : access Sax_Reader'Class;
|
|
Ch : Unicode.CES.Byte_Sequence) is
|
|
begin
|
|
Internal_Characters (Validating_Reader_Access (Handler), Ch);
|
|
end Hook_Characters;
|
|
|
|
-------------------------------
|
|
-- Hook_Ignorable_Whitespace --
|
|
-------------------------------
|
|
|
|
procedure Hook_Ignorable_Whitespace
|
|
(Handler : access Sax_Reader'Class;
|
|
Ch : Unicode.CES.Byte_Sequence)
|
|
is
|
|
H : constant Validating_Reader_Access :=
|
|
Validating_Reader_Access (Handler);
|
|
NFA : constant Schema_NFA_Access := Get_NFA (H.Grammar);
|
|
S : State;
|
|
Descr : access Type_Descr;
|
|
Iter : Active_State_Iterator := For_Each_Active_State
|
|
(H.Matcher, Ignore_If_Nested => True, Ignore_If_Default => True);
|
|
|
|
begin
|
|
loop
|
|
S := Current (H.Matcher, Iter);
|
|
exit when S = No_State;
|
|
|
|
Descr := Get_Type_Descr (NFA, Current_Data (H.Matcher, Iter).Simple);
|
|
if Descr.Simple_Content /= No_Simple_Type_Index
|
|
or else Descr.Mixed
|
|
then
|
|
Internal_Characters (H, Ch);
|
|
return;
|
|
end if;
|
|
|
|
Next (H.Matcher, Iter);
|
|
end loop;
|
|
end Hook_Ignorable_Whitespace;
|
|
|
|
-----------
|
|
-- Reset --
|
|
-----------
|
|
|
|
procedure Reset (Parser : in out Validating_Reader) is
|
|
begin
|
|
-- Save current location, for retrieval by Get_Error_Message
|
|
Free (Parser.Id_Table);
|
|
Free (Parser.Matcher);
|
|
Free (Parser.Characters);
|
|
Parser.Characters_Count := 0;
|
|
end Reset;
|
|
|
|
-----------
|
|
-- Parse --
|
|
-----------
|
|
|
|
overriding procedure Parse
|
|
(Parser : in out Validating_Reader;
|
|
Input : in out Input_Sources.Input_Source'Class) is
|
|
begin
|
|
if Debug then
|
|
Output_Action
|
|
("Parsing XML file " & Input_Sources.Get_System_Id (Input));
|
|
end if;
|
|
|
|
Initialize_Symbols (Parser);
|
|
Initialize_Grammar (Parser);
|
|
|
|
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,
|
|
Notation_Decl => Hook_Notation_Decl'Access);
|
|
Free (Parser.Matcher);
|
|
else
|
|
Set_Hooks (Parser,
|
|
Start_Element => null,
|
|
End_Element => null,
|
|
Characters => null,
|
|
Whitespace => null,
|
|
Doc_Locator => null);
|
|
end if;
|
|
|
|
-- Not a dispatching call
|
|
Parse (Schema.Validators.Abstract_Validation_Reader (Parser), Input);
|
|
|
|
if not In_Final (Parser.Matcher) then
|
|
Validation_Error
|
|
(Parser'Access,
|
|
"Unexpected end of file: expecting "
|
|
& Expected (Parser.Matcher));
|
|
end if;
|
|
|
|
Reset (Parser);
|
|
|
|
exception
|
|
when others =>
|
|
Reset (Parser);
|
|
raise;
|
|
end Parse;
|
|
|
|
-------------------------------
|
|
-- Get_Namespace_From_Prefix --
|
|
-------------------------------
|
|
|
|
procedure Get_Namespace_From_Prefix
|
|
(Handler : in out Validating_Reader;
|
|
Prefix : Symbol;
|
|
NS : out Sax.Utils.XML_NS) is
|
|
begin
|
|
Find_NS
|
|
(Parser => Handler,
|
|
Prefix => Prefix,
|
|
NS => NS);
|
|
if Get_URI (NS) = Empty_String then
|
|
NS := No_XML_NS;
|
|
end if;
|
|
end Get_Namespace_From_Prefix;
|
|
|
|
----------
|
|
-- Free --
|
|
----------
|
|
|
|
procedure Free (Reader : in out Validating_Reader_Access) is
|
|
procedure Unchecked_Free is new Ada.Unchecked_Deallocation
|
|
(Validating_Reader'Class, Validating_Reader_Access);
|
|
begin
|
|
if Reader /= null then
|
|
Free (Reader.all);
|
|
Unchecked_Free (Reader);
|
|
end if;
|
|
end Free;
|
|
|
|
----------
|
|
-- Free --
|
|
----------
|
|
|
|
overriding procedure Free (Reader : in out Validating_Reader) is
|
|
begin
|
|
Free (Schema.Validators.Abstract_Validation_Reader (Reader));
|
|
end Free;
|
|
|
|
-------------------
|
|
-- Parse_Grammar --
|
|
-------------------
|
|
|
|
procedure Parse_Grammar
|
|
(Handler : not null access Validating_Reader;
|
|
URI : Sax.Symbols.Symbol;
|
|
Xsd_File : Sax.Symbols.Symbol;
|
|
Do_Create_NFA : Boolean := True) is
|
|
begin
|
|
Schema.Schema_Readers.Parse_Grammar_If_Needed
|
|
(Handler, URI, Xsd_File, Do_Create_NFA);
|
|
end Parse_Grammar;
|
|
|
|
end Schema.Readers;
|