1:- module(validate, [
    2		validate/2,
    3		validate/3,
    4		xsd_table/2,
    5		cleanup/0
    6	]).    7
    8:- use_module(library(xsd/xsd_helper)).    9:- use_module(library(xsd/simpletype)).   10:- use_module(library(xsd/flatten)).   11
   12:- use_module(library(statistics)).   13:- use_module(library(settings)).   14:- setting('without-tabling', boolean, false, 'Run without tabling').   15:- setting(profile, boolean, false, 'Show profile').   16
   17
   18/*
   19	validate_tabled/6
   20	
   21	Tabling for validate/5
   22	Checks, wether validate/6 was previously called with the given parameters, 
   23	then the result is already saved as a xsd_table/2 fact. 
   24	otherwise, validate/6 is called and the result is saved to avoid double calculations
   25*/
   26:- dynamic xsd_table/2.   27validate_tabled(D_File, D_ID, Validated_Nodes, S_File, S_ID) :-
   28	setting('without-tabling', true),
   29	!,
   30	validate(D_File, D_ID, Validated_Nodes, S_File, S_ID).
   31
   32validate_tabled(D_File, D_ID, Validated_Nodes, S_File, S_ID) :-
   33	(xsd_table(validate(D_File, D_ID, Validated_Nodes, S_File, S_ID), Valid) ->
   34		!, call(Valid)
   35	;
   36		(validate(D_File, D_ID, Validated_Nodes, S_File, S_ID) ->
   37			asserta(xsd_table(validate(D_File, D_ID, Validated_Nodes, S_File, S_ID), true))
   38		;
   39			asserta(xsd_table(validate(D_File, D_ID, Validated_Nodes, S_File, S_ID), false)),
   40			!,
   41			false
   42		)
   43	).
   44cleanup :-
   45	retractall(xsd_table(_,_)).
   46
   47/*
   48	validate/2
   49	Validates given XML document `D_File` against XSD schema `S_File`. 
   50	(Both files must be loaded using `flatten_xml/2`)
   51	
   52	?- validate(xml_file, xsd_file).
   53*/
   54validate(S_File, D_File) :-
   55	validate(S_File, D_File, []).
   56
   57validate(S_File, D_File, Options) :-
   58	set_options(Options),
   59	(setting(profile, true) ->
   60		time(validate_tabled(D_File, [0], 1, S_File, [0]))
   61	;	validate_tabled(D_File, [0], 1, S_File, [0])),
   62%	validate_tabled(D_File, [0], 1, S_File, [0]),
   63	% only one solution
   64	!,
   65	set_default_options(Options).
   66
   67set_options(Options) :-
   68	maplist(set_option, Options),
   69	!.
   70
   71set_option(Option) :-
   72	Option =.. [Key, Value],
   73	current_setting(Key),
   74	set_setting(Key, Value).
   75set_option(_Option) :-
   76	true.
   77
   78set_default_options(Options) :-
   79	maplist(set_default_option, Options),
   80	!.
   81set_default_option(Option) :-
   82	Option =.. [Key, _Value],
   83	current_setting(Key),
   84	restore_setting(Key).
   85set_default_option(_Option) :-
   86	true.
   87
   88
   89/*
   90	schema
   91*/
   92validate(D_File, D_ID, 1, S_File, S_ID) :-
   93	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), schema),
   94	% validate: D_ID and one child 'element' in schema
   95	child(S_File, S_ID, S_Child_ID),
   96	node(S_File, S_Child_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), element),
   97	validate_tabled(D_File, D_ID, 1, S_File, S_Child_ID).
   98
   99/* 
  100	ref
  101	Resolves references to other elements in the document using the attribute ref
  102*/
  103validate(D_File, D_ID, Validated_Nodes, S_File, S_ID) :-
  104	attribute(S_File, S_ID, ref, S_QName),
  105
  106	attribute(S_File, S_ID0, name, S_QName),
  107	validate_tabled(D_File, D_ID, Validated_Nodes, S_File, S_ID0).
  108
  109/*
  110	element
  111*/
  112% minOccurs = 0
  113validate(_D_File, _D_ID, 0, S_File, S_ID) :-
  114	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), element), 
  115	attribute(S_File, S_ID, minOccurs, '0').
  116% minOccurs =< # of elements =< maxOccurs
  117validate(D_File, D_ID, Validated_Nodes, S_File, S_ID) :-
  118	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), element),
  119	% Min/MaxOccurs
  120	attribute(S_File, S_ID, minOccurs, MinOccurs),
  121	atom_number(MinOccurs, Min),
  122	attribute(S_File, S_ID, maxOccurs, MaxOccurs),
  123	(
  124	MaxOccurs = unbounded ->
  125		count_remaining_siblings(D_File, D_ID, Max)
  126		;
  127		atom_number(MaxOccurs, Max)
  128	),
  129	% Validated_Nodes in Range
  130	between(Min, Max, Validated_Nodes),
  131	% Validate each Element Node
  132	forall(between(1, Validated_Nodes, Nth), 
  133		(get_nth_sibling(D_File, D_ID, Nth, Nth_ID), validate_element(D_File, Nth_ID, S_File, S_ID))).
  134
  135/*
  136	complexType
  137*/
  138validate(D_File, D_ID, 1, S_File, S_ID) :-
  139	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), complexType),
  140	child(S_File, S_ID, S_Type_ID),
  141	node(S_File, S_Type_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), S_Type),
  142	
  143	member(S_Type, [sequence, choice, all]),
  144	validate_all_attributes(D_File, D_ID, S_File, S_ID),
  145	
  146	count_children(D_File, D_ID, N_Children),
  147	(
  148	N_Children = 0 ->
  149		% no children -> validate schema against no element (equals non-existing element -> [1])
  150		validate_tabled(D_File, [1], 0, S_File, S_Type_ID)
  151	;
  152		% validate all N_Children otherwise
  153		get_nth_child(D_File, D_ID, 1, Child_ID),
  154		validate_tabled(D_File, Child_ID, N_Children, S_File, S_Type_ID)
  155	).
  156% empty complexType (except attributes)
  157validate(D_File, D_ID, 1, S_File, S_ID) :-
  158	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), complexType),
  159	% no content defining children
  160	forall((child(S_File, S_ID, S_Type_ID), member(S_Type, [sequence, choice, all])),
  161		\+node(S_File, S_Type_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), S_Type)),
  162	
  163	% no children in document
  164	count_children(D_File, D_ID, 0),
  165	validate_all_attributes(D_File, D_ID, S_File, S_ID).
  166
  167/*
  168	simpleType
  169*/
  170% simpleType as content of nodes (actual validation of content or attribute values handled by `validate_simpleType/3`)
  171validate(D_File, D_ID, 1, S_File, S_ID) :-
  172	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), simpleType),
  173	
  174	% no attributes in xml file, 1 or 0 children
  175	\+attribute(D_File, D_ID, _Name, _Value), 
  176	(
  177		count_children(D_File, D_ID, 1),
  178		child(D_File, D_ID, D_Child), 
  179		text_node(D_File, D_Child, D_Text)
  180		;
  181		count_children(D_File, D_ID, 0),
  182		D_Text = ''
  183	),
  184	validate_simpleType(D_Text, S_File, S_ID). 
  185
  186/*
  187	sequence
  188*/
  189validate(D_File, D_ID, Validated_Nodes, S_File, S_ID) :-
  190	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), sequence),
  191	get_n_siblings(D_File, D_ID, Validated_Nodes, D_Nodes),
  192	get_children(S_File, S_ID, S_Children),
  193	
  194	% Min/MaxOccurs
  195	attribute(S_File, S_ID, minOccurs, MinOccurs),
  196	atom_number(MinOccurs, Min),
  197	attribute(S_File, S_ID, maxOccurs, MaxOccurs),
  198	(
  199	MaxOccurs = unbounded ->
  200		count_remaining_siblings(D_File, D_ID, Max)
  201		;
  202		atom_number(MaxOccurs, Max)
  203	),
  204	validate_sequence(D_File, D_Nodes, S_File, S_Children, S_Children, Min, Max).
  205
  206/*
  207	choice
  208*/
  209validate(D_File, D_ID, Validated_Nodes, S_File, S_ID) :-
  210	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), choice),
  211	get_n_siblings(D_File, D_ID, Validated_Nodes, D_Nodes),
  212	get_children(S_File, S_ID, S_Children),
  213	
  214	% Min/MaxOccurs
  215	attribute(S_File, S_ID, minOccurs, MinOccurs),
  216	atom_number(MinOccurs, Min),
  217	attribute(S_File, S_ID, maxOccurs, MaxOccurs),
  218	(
  219	MaxOccurs = unbounded ->
  220		count_remaining_siblings(D_File, D_ID, Max)
  221		;
  222		atom_number(MaxOccurs, Max)
  223	),
  224	
  225	validate_choice(D_File, D_Nodes, S_File, S_Children, Min, Max).
  226
  227/*
  228	all
  229*/
  230validate(D_File, D_ID, N_Siblings, S_File, S_ID) :-
  231	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), all), 
  232	attribute(S_File, S_ID, maxOccurs, '1'),
  233	get_children(S_File, S_ID, S_IDs), 
  234	count_remaining_siblings(D_File, D_ID, N_Siblings),
  235	get_n_siblings(D_File, D_ID, N_Siblings, D_IDs),
  236	validate_all(D_File, D_IDs, S_File, S_IDs). 
  237validate(_D_File, _D_ID, 0, S_File, S_ID) :-
  238	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), all), 
  239	attribute(S_File, S_ID, minOccurs, '0').
  240
  241/*
  242	#### (End of validate/5) ####
  243*/	
  244
  245/*
  246	attribute
  247*/
  248validate_all_attributes(D_File, D_ID, S_File, S_ID) :-
  249	get_children(S_File, S_ID, S_Children), 
  250	findall(S_Child, 
  251		(member(S_Child, S_Children), node(S_File, S_Child, ns(_, 'http://www.w3.org/2001/XMLSchema'), attribute)),
  252		S_Attribute_IDs),
  253	findall(attribute(D_File, D_ID, Name, Value), 
  254		attribute(D_File, D_ID, Name, Value), 
  255		D_Attribute_List),
  256	validate_attributes(D_File, D_Attribute_List, S_File, S_Attribute_IDs).
  257
  258/* 
  259	validate_element/4
  260	validate_element(D_File, D_ID, S_File, S_ID)
  261	
  262	Validates a single element `D_ID` from file `D_File` against schema node `S_ID` from `S_File`.
  263	
  264	Is called by validate(element,_,_,_,_,_). (min/maxOccurs is handled there)
  265	
  266*/
  267% no type
  268validate_element(D_File, D_ID, S_File, S_ID) :-
  269	validate_element_name(D_File, D_ID, S_File, S_ID),
  270	
  271	get_children(S_File, S_ID, []),
  272	\+attribute(S_File, S_ID, type, _),
  273	\+attribute(S_File, S_ID, ref, _),
  274	
  275	get_children(D_File, D_ID, []),
  276	\+attribute(D_File, D_ID, _Name, _Value).
  277% xsd simple Type
  278validate_element(D_File, D_ID, S_File, S_ID) :-
  279	validate_element_name(D_File, D_ID, S_File, S_ID),
  280	% no child nodes in Schema
  281	get_children(S_File, S_ID, []),
  282	attribute(S_File, S_ID, type, S_Type_NameNS),
  283	namespace(S_Type_NameNS, NS_Prefix, S_Type_Name),
  284	resolve_namespace(S_File, S_ID, NS_Prefix, 'http://www.w3.org/2001/XMLSchema'),
  285	(
  286		% one child node in Document (is text node with ID = Child_Node)
  287		get_children(D_File, D_ID, [Child_Node]),
  288		text_node(D_File, Child_Node, D_Text)
  289		;
  290		% otherwise: no children
  291		get_children(D_File, D_ID, []),
  292		D_Text = ''
  293	),
  294	\+attribute(D_File, D_ID, _Name, _Value),
  295	validate_xsd_simpleType(S_Type_Name, D_Text).
  296validate_element(D_File, D_ID, S_File, S_ID) :-
  297	validate_element_name(D_File, D_ID, S_File, S_ID),
  298	(
  299		% nested type: type definition in child node
  300		child(S_File, S_ID, S_Type_ID)
  301		;
  302		% global type: type definition anywhere in document
  303		attribute(S_File, S_ID, type, S_User_Type), 
  304		attribute(S_File, S_Type_ID, name, S_User_Type)
  305	), 
  306	% S_Type: [complexType, simpleType]
  307	node(S_File, S_Type_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), S_Type),
  308	member(S_Type, [complexType, simpleType]),
  309	validate_tabled(D_File, D_ID, 1, S_File, S_Type_ID).
  310
  311/*
  312	validate_element_name/4
  313	
  314	Validates name of `D_ID` against specified name in `S_ID`
  315*/
  316validate_element_name(D_File, D_ID, S_File, S_ID) :-
  317	% check Name (currently: ignoring namespaces; TODO (?))
  318	node(D_File, D_ID, _D_NS, D_Name),
  319	attribute(S_File, S_ID, name, D_Name).
  320
  321/*
  322	validate_simpleType
  323*/
  324% find simpleType by name `S_Type` and validate (S_ID only for namespace handling)
  325validate_simpleType(D_Text, S_File, _S_ID, S_Type) :-
  326	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), simpleType),
  327	attribute(S_File, S_ID, name, S_Type),
  328	validate_simpleType(D_Text, S_File, S_ID).
  329validate_simpleType(D_Text, S_File, S_ID, S_Type) :-
  330	namespace(S_Type, NS_Prefix, S_Type_Name),
  331	resolve_namespace(S_File, S_ID, NS_Prefix, 'http://www.w3.org/2001/XMLSchema'),
  332	validate_xsd_simpleType(S_Type_Name, D_Text).
  333% restriction
  334validate_simpleType(D_Text, S_File, S_ID) :-
  335	child(S_File, S_ID, S_Child),
  336	node(S_File, S_Child, ns(_, 'http://www.w3.org/2001/XMLSchema'), restriction),
  337	
  338	attribute(S_File, S_Child, base, S_Type),
  339	validate_simpleType(D_Text, S_File, S_ID, S_Type),
  340	
  341	get_children(S_File, S_Child, S_Facets), 
  342	validate_restriction(D_Text, S_File, S_Facets).
  343% union
  344validate_simpleType(D_Text, S_File, S_ID) :-
  345	child(S_File, S_ID, S_Child),
  346	node(S_File, S_Child, ns(_, 'http://www.w3.org/2001/XMLSchema'), union),
  347	% types as memberTypes-attribute
  348	attribute(S_File, S_Child, memberTypes, S_Types), 
  349	atomic_list_concat(Types_List, ' ', S_Types),
  350	member(S_Type, Types_List),
  351	validate_simpleType(D_Text, S_File, S_ID, S_Type).
  352validate_simpleType(D_Text, S_File, S_ID) :-
  353	child(S_File, S_ID, S_Child),
  354	node(S_File, S_Child, ns(_, 'http://www.w3.org/2001/XMLSchema'), union),
  355	% types as children
  356	get_children(S_File, S_Child, S_SimpleTypes), 
  357	member(S_SimpleType, S_SimpleTypes),
  358	node(S_File, S_SimpleType, ns(_, 'http://www.w3.org/2001/XMLSchema'), simpleType),
  359	validate_simpleType(D_Text, S_File, S_SimpleType). 
  360% list
  361validate_simpleType(D_Text, S_File, S_ID) :-
  362	child(S_File, S_ID, S_Child),
  363	node(S_File, S_Child, ns(_, 'http://www.w3.org/2001/XMLSchema'), list),
  364	% type as itemType-attribute
  365	attribute(S_File, S_Child, itemType, S_Type), 
  366	atomic_list_concat(D_Items, ' ', D_Text),
  367	subtract(D_Items, [''], D_Items0),
  368	forall(member(D_Item, D_Items0), validate_simpleType(D_Item, S_File, S_ID, S_Type)).
  369validate_simpleType(D_Text, S_File, S_ID) :-
  370	child(S_File, S_ID, S_Child),
  371	node(S_File, S_Child, ns(_, 'http://www.w3.org/2001/XMLSchema'), list),
  372	% type as child
  373	child(S_File, S_Child, S_SimpleType), 
  374	node(S_File, S_SimpleType, ns(_, 'http://www.w3.org/2001/XMLSchema'), simpleType),
  375	atomic_list_concat(D_Items, ' ', D_Text),
  376	subtract(D_Items, [''], D_Items0),
  377	forall(member(D_Item, D_Items0), validate_simpleType(D_Item, S_File, S_SimpleType)).
  378	
  379/*
  380	validate_restriction
  381*/
  382validate_restriction(_D_Text, _S_File, []).
  383% min/max facets
  384validate_restriction(D_Text, S_File, [S_ID|S_IDs]) :-
  385	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), Facet), 
  386	attribute(S_File, S_ID, value, Val),
  387	facet(Facet, Val, D_Text),
  388	validate_restriction(D_Text, S_File, S_IDs).
  389% enumeration
  390validate_restriction(D_Text, S_File, S_IDs) :-
  391	findall(S_ID, 
  392		(member(S_ID, S_IDs), node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), enumeration)),
  393		Enum_IDs),
  394	member(Enum_ID, Enum_IDs), 
  395	attribute(S_File, Enum_ID, value, D_Text),
  396	
  397	subtract(S_IDs, Enum_IDs, S_IDs0),
  398	validate_restriction(D_Text, S_File, S_IDs0). 
  399
  400/*
  401	validate_sequence/6
  402	validate_sequence(D_File, D_Remaining_IDs, S_File, S_Remaining_IDs, S_IDs, MinOccurs, MaxOccurs)
  403	
  404	Validates a list of document nodes `D_Remaining_IDs` against a list of schema nodes `S_Remaining_IDs`.
  405	There must be a corresponding number of document nodes for each schema node in the given order.
  406	
  407	
  408	Is called by validate(sequence,_,_,_,_,_)
  409*/
  410
  411% minOccurs = 0 / end of recursion
  412validate_sequence(_D_File, [], _S_File, S_Remaining_IDs, S_IDs, Min, Max) :-
  413	member(S_Remaining_IDs, [[], S_IDs]),
  414	Min =< 0,
  415	Max >= 0.
  416validate_sequence(D_File, [], S_File, S_Remaining_IDs, S_IDs, Min, _Max) :-
  417	% empty sequence -> every element in sequence validates against zero elements
  418	forall(member(S_ID, S_Remaining_IDs), (node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), S_Type), validate_tabled(D_File, [], 0, S_File, S_ID))),
  419	(
  420		Min =< 1
  421		;
  422		forall(member(S_ID, S_IDs), (node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), S_Type), validate_tabled(D_File, null, 0, S_File, S_ID)))
  423	).
  424validate_sequence(D_File, D_IDs, S_File, [S_ID], S_IDs, Min, Max) :-
  425	Max > 0, 
  426	D_IDs = [D_ID|_],
  427	%
  428	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), S_Type),
  429	member(S_Type, [element, sequence, choice]),
  430	length(D_IDs, D_Remaining),
  431	between(0, D_Remaining, Val_Nodes),
  432	validate_tabled(D_File, D_ID, Val_Nodes, S_File, S_ID),
  433	length(TempList, Val_Nodes),
  434	append(TempList, D_IDs0, D_IDs),
  435	
  436	% reset S_Remaining_IDs and validate next sequence
  437	Min0 is Min - 1,
  438	Max0 is Max - 1,
  439	validate_sequence(D_File, D_IDs0, S_File, S_IDs, S_IDs, Min0, Max0).
  440validate_sequence(D_File, D_IDs, S_File, S_Remaining_IDs, S_IDs, Min, Max) :-
  441	Max > 0, 
  442	% Validate `Val_Nodes` many document nodes against next schema node
  443	D_IDs = [D_ID|_],
  444	S_Remaining_IDs = [S_ID|S_Remaining_IDs0],
  445	S_Remaining_IDs0 \= [],
  446	!,
  447	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), S_Type),
  448	member(S_Type, [element, sequence, choice]),
  449	length(D_IDs, D_Remaining),
  450	between(0, D_Remaining, Val_Nodes),
  451	validate_tabled(D_File, D_ID, Val_Nodes, S_File, S_ID),
  452	length(TempList, Val_Nodes),
  453	append(TempList, D_IDs0, D_IDs),
  454	%
  455
  456	validate_sequence(D_File, D_IDs0, S_File, S_Remaining_IDs0, S_IDs, Min, Max).
  457
  458/*
  459	validate_choice/7
  460	validate_choice(D_File, D_Nodes, S_File, S_Children, S_Children, Min, Max)
  461*/
  462% end of recursion & minOccurs = 0 
  463validate_choice(_D_File, [], _S_File, _S_IDs, Min, Max) :-
  464	Min =< 0,
  465	Max >= 0.
  466% empty choice declaration
  467validate_choice(_D_File, [], _S_File, [], _Min, _Max).
  468% no document nodes -> one element in choice validates against zero elements in document
  469validate_choice(D_File, [], S_File, S_IDs, _Min, _Max) :-
  470	member(S_ID, S_IDs),
  471	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), S_Type),
  472	member(S_Type, [element, choice, sequence]),
  473	validate_tabled(D_File, [1], 0, S_File, S_ID).
  474validate_choice(D_File, D_IDs, S_File, S_IDs, Min, Max) :-
  475	Max > 0,
  476	% Validate `Val_Nodes` many document nodes against schema node
  477	D_IDs = [D_ID|_],
  478	member(S_ID, S_IDs),
  479	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), S_Type),
  480	member(S_Type, [element, choice, sequence]),
  481	length(D_IDs, D_Remaining),
  482	between(0, D_Remaining, Val_Nodes),
  483	validate_tabled(D_File, D_ID, Val_Nodes, S_File, S_ID),
  484	length(TempList, Val_Nodes),
  485	append(TempList, D_IDs0, D_IDs),
  486	
  487	Min0 is Min - 1,
  488	Max0 is Max - 1,
  489	validate_choice(D_File, D_IDs0, S_File, S_IDs, Min0, Max0).
  490	
  491/*
  492	validate_all/4
  493	validate_sequence(D_File, D_IDs, S_File, S_IDs)
  494	
  495	Validates a list of document nodes `D_IDs` against a list of schema nodes `S_IDs`. 
  496	Without regard to the order, each given document node must validate against one schema node. 
  497*/
  498validate_all(_D_File, [], _S_File, []).
  499validate_all(D_File, [], S_File, S_IDs)	:-
  500	forall(member(S_ID, S_IDs), (node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), _S_Type), validate_tabled(D_File, null, 0, S_File, S_ID))).
  501validate_all(D_File, [D_ID|D_IDs], S_File, S_IDs) :-
  502	member(S_ID, S_IDs),
  503	node(S_File, S_ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), element),
  504	validate_tabled(D_File, D_ID, 1, S_File, S_ID),
  505	delete(S_IDs, S_ID, S_IDs0),
  506	validate_all(D_File, D_IDs, S_File, S_IDs0).
  507
  508/*
  509	validate_attributes/4
  510	validate_attributes(D_File, D_Attribute_List, S_File, S_Attribute_IDs)
  511	
  512	`D_Attribute_List`: List of `attribute/4` nodes.
  513	`S_Attribute_IDs`: List of IDs correspoding to <xs:attribute .. /> nodes
  514*/
  515validate_attributes(_D_File, [], _S_File, []).	
  516validate_attributes(_D_File, [], S_File, S_Attribute_IDs) :-
  517	forall(member(S_ID, S_Attribute_IDs), 
  518		\+attribute(S_File, S_ID, use, 'required')).
  519
  520validate_attributes(D_File, [D_Attribute|D_Attributes], S_File, S_Attribute_IDs) :-
  521	D_Attribute = attribute(D_File, _D_ID, Name, _Value),
  522
  523	% skip xmlns attributes
  524	( Name = xmlns ; Name = xmlns:_ ),
  525
  526	validate_attributes(D_File, D_Attributes, S_File, S_Attribute_IDs).	
  527
  528validate_attributes(D_File, [D_Attribute|D_Attributes], S_File, S_Attribute_IDs) :-
  529	D_Attribute = attribute(D_File, _D_ID, Name, _Value),
  530
  531	member(S_ID, S_Attribute_IDs),
  532	attribute(S_File, S_ID, name, Name),
  533	\+attribute(S_File, S_ID, use, 'prohibited'),
  534
  535	validate_attribute(D_Attribute, S_File, S_ID),
  536
  537	delete(S_Attribute_IDs, S_ID, S_Attribute_IDs0),
  538	validate_attributes(D_File, D_Attributes, S_File, S_Attribute_IDs0).
  539
  540validate_attribute(D_Attribute, S_File, S_ID) :-
  541	D_Attribute = attribute(_D_File, _D_ID, _Name, D_Value),
  542	% check fixed values
  543	(attribute(S_File, S_ID, fixed, S_FixedVal) ->
  544		S_FixedVal = D_Value
  545		;
  546		true
  547	),
  548	% validate simpleType (reference or nested)
  549	(
  550		attribute(S_File, S_ID, type, S_Type),
  551		validate_simpleType(D_Value, S_File, S_ID, S_Type)
  552	;
  553		child(S_File, S_ID, S_Child),
  554		validate_simpleType(D_Value, S_File, S_Child)
  555	).
  556
  557
  558
  559/*
  560	attribute/4
  561	attribute(File_ID, ID, Attribute_Name, Value)
  562	
  563	Determines the attribute value `Value` of given `File_ID`, `ID` and `Attribute_Name` using `node_attribute/4`. 
  564		Specifies all permitted attributes, including default ones, if `Attribute_Name` is left empty.
  565	
  566	Currently, only some (minOccurs, maxOccurs, use) XML-Schema-Defaults are supported.
  567*/
  568attribute(File_ID, ID, Attribute_Name, Value) :-
  569	node_attribute(File_ID, ID, Attribute_Name, Value),
  570	Attribute_Name \= _NS:_Name.
  571
  572% XML-Schema Defaults
  573attribute(File_ID, ID, minOccurs, '1') :-
  574	\+node_attribute(File_ID, ID, minOccurs, _),
  575	% Check type and namespace
  576	member(Element_Type, [element, choice, sequence, all]),
  577	node(File_ID, ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), Element_Type).
  578	
  579attribute(File_ID, ID, maxOccurs, '1') :-
  580	\+node_attribute(File_ID, ID, maxOccurs, _),
  581	% Check type and namespace
  582	member(Element_Type, [element, choice, sequence, all]),
  583	node(File_ID, ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), Element_Type).
  584	
  585attribute(File_ID, ID, use, 'optional') :-
  586	\+node_attribute(File_ID, ID, use, _), 
  587	% Check type and namespace
  588	member(Element_Type, [attribute]),
  589	node(File_ID, ID, ns(_, 'http://www.w3.org/2001/XMLSchema'), Element_Type)