📄 xmerl_xsd.erl
字号:
%%% The contents of this file are subject to the Erlang Public License,%%% Version 1.0, (the "License"); you may not use this file except in%%% compliance with the License. You may obtain a copy of the License at%%% http://www.erlang.org/license/EPL1_0.txt%%%%%% Software distributed under the License is distributed on an "AS IS"%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%%% the License for the specific language governing rights and limitations%%% under the License.%%%%%%%%% The Initial Developer of the Original Code is Ericsson Telecom%%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson%%% Telecom AB. All Rights Reserved.%%%%%% Contributor(s): ______________________________________.%%%%%%-------------------------------------------------------------------%%% File : xmerl_xsd.erl%%% Author : Bertil Karlsson <bertil.karlsson@ericsson.com>%%% Description : %%%%%%%%%-------------------------------------------------------------------%% @author Bertil Karlsson%% @doc Interface module for XML Schema vlidation. %% It handles the W3.org %% <a href="http://www.w3.org/XML/Schema#dev">specifications</a>%% of XML Schema second edition 28 october 2004. For an introduction to%% XML Schema study <a href="http://www.w3.org/TR/xmlschema-0/">part 0.</a>%% An XML structure is validated by xmerl_xsd:validate/[2,3].%% @type global_state(). <p>The global state of the validator. It is %% representated by the <code>#xmerl_xsd{}</code> record.%% </p>%% @type option_list(). <p>Options allow to customize the behaviour of the %% validation.%% </p>%% Possible options are :%% <dl>%% <dt><code>{tab2file,boolean()}</code></dt>%% <dd>Enables saving of abstract structure on file for debugging%% purpose.</dd>%% <dt><code>{xsdbase,filename()}</code></dt>%% <dd>XSD Base directory.</dd>%% <dt><code>{fetch_fun,FetchFun}</code></dt>%% <dd>Call back function to fetch an external resource.</dd>%% <dt><code>{fetch_path,PathList}</code></dt>%% <dd>PathList is a list of directories to search when fetching files.%% If the file in question is not in the fetch_path, the URI will%% be used as a file name.</dd>%% <dt><code>{state,State}</code></dt>%% <dd>It is possible by this option to provide a state with process%% information from an earlier validation.</dd> %% </dl>-module(xmerl_xsd).-export([validate/2,validate/3,process_validate/2,process_validate/3, process_schema/1,process_schema/2, process_schemas/1,process_schemas/2, state2file/1,state2file/2,file2state/1,format_error/1]).-export([print_table/1]).%%-export([whitespace/1]).-include("xmerl.hrl").-include("xmerl_xsd.hrl").-include_lib("kernel/include/file.hrl").-import(xmerl_lib,[is_facet/1, is_builtin_simple_type/1]).-import(xmerl_xsd_type,[facet_fun/2]).-import(lists,[reverse/1,reverse/2,foldl/3,member/2,filter/2,flatten/1,map/2, splitwith/2,mapfoldl/3,keysearch/3,keymember/3, keyreplace/4,keydelete/3]).%% @spec validate(Element,State) -> Result%% @equiv validate(Element,State,[])validate(Xml,State) -> validate(Xml,State,[]).%% @spec validate(Element,State,Options) -> Result%% Element = XmlElement%% Options = option_list() %% Result = {ValidElement,global_state()} | {error,Reasons}%% ValidElement = XmlElement%% State = global_state()%% Reasons = [ErrorReason] | ErrorReason%% @doc Validates a parsed well-formed XML element (Element).%% <p>A call to validate/2 or validate/3 must provide a well formed %% parsed XML element <code>#xmlElement{}</code> and a State,%% <code>global_state()</code>, which holds necessary information from%% an already processed schema.%% Thus validate enables reuse of the schema information and%% therefore if one shall validate several times towards the same%% schema it reduces time consumption.</p>%% <p>The result, ValidElement, is the valid element that conforms to the %% post-schema-validation infoset. When the validator finds an error it%% tries to continue and reports a list of all errors found. In those cases%% an unexpected error is found it may cause a single error reason.%% </p>%% <p> Usage example:</p>%% <p>%% <code>1>{E,_} = xmerl_scan:file("my_XML_document.xml").</code><br/>%% <code>2>{ok,S} = xmerl_xsd:process_schema("my_XML_Schema.xsd").</code><br/>%% <code>3>{E2,_} = xmerl_xsd:validate(E,S).</code>%% </p>%% <p> Observe that E2 may differ from E if for instance there are default%% values defined in <code>my_XML_Schema.xsd</code>.</p>validate(Xml,State,Opts) when is_record(State,xsd_state) -> S2 = initiate_state2(State,Opts), S3 = validation_options(S2,Opts), validate3(S3#xsd_state.schema_name,Xml,S3).%% @spec state2file(State) -> ok | {error,Reason}%% @doc Same as state2file(State,SchemaName)%%%% The name of the saved file is the same as the name of the%% schema, but with <code>.xss</code> extension.state2file(S=#xsd_state{schema_name=SN}) -> state2file(S,filename:rootname(SN)).%% @spec state2file(State,FileName) -> ok | {error,Reason}%% State = global_state()%% FileName = filename()%% @doc Saves the schema state with all information of the processed%% schema in a file. You can provide the file name for the saved%% state. FileName is saved with the <code>.xss</code> extension%% added.state2file(S,FileName) -> save_xsd_state(S), case catch ets:tab2file(S#xsd_state.table,lists:append(FileName,".xss")) of {'EXIT',Reason} -> {error,{[],?MODULE,Reason}}; Ret -> Ret end.%% @spec file2state(FileName) -> {ok,State} | {error,Reason}%% State = global_state()%% FileName = filename()%% @doc Reads the schema state with all information of the processed%% schema from a file created with <code>state2file/[1,2]</code>. The%% format of this file is internal. The state can then be used%% validating an XML document.file2state(FileName) -> case catch ets:file2tab(FileName) of {ok,Tab} -> case load_xsd_state(Tab) of [{state,S}] when is_record(S,xsd_state) -> xmerl_xsd_vsn_check(S);%% {ok,S}; Other -> {error,{[],?MODULE,{incomplete_file,FileName,Other}}} end; {error,Reason} -> {error,{[],?MODULE,Reason}}; Other -> {error,{[],?MODULE,Other}} end.save_xsd_state(S) -> catch ets:insert(S#xsd_state.table,{state,S}).load_xsd_state(Table) -> catch ets:lookup(Table,state). xmerl_xsd_vsn() -> case lists:keysearch(vsn,1,xmerl_xsd:module_info(attributes)) of {value,{_,MD5_VSN}} -> MD5_VSN; _ -> undefined end.xmerl_xsd_vsn_check(S=#xsd_state{vsn=MD5_VSN}) -> case [V||{vsn,V}<-xmerl_xsd:module_info(attributes)] of [MD5_VSN] -> {ok,S}; _ -> {error,{[],?MODULE,{different_version_of_xmerl_xsd_module_used, state_not_reliable}}} end. %% @spec process_validate(Schema,Element) -> Result%% @equiv process_validate(Schema,Xml,[])process_validate(Schema,Xml) -> process_validate(Schema,Xml,[]).%% @spec process_validate(Schema,Element,Options) -> Result%% Schema = filename()%% Element = XmlElement%% Options = option_list()%% Result = {ValidXmlElement,State} | {error,Reason}%% Reason = [ErrorReason] | ErrorReason%% @doc Validates a parsed well-formed XML element towards an XML%% schema. <p> Validates in two steps. First it processes the schema,%% saves the type and structure info in an ets table and then%% validates the element towards the schema.</p>%% <p> Usage example:</p>%% <p>%% <code>1>{E,_} = xmerl_scan:file("my_XML_document.xml").</code><br/>%% <code>2>{E2,_} = xmerl_xsd:validate("my_XML_Schema.xsd",E).</code>%% </p>%% <p> Observe that E2 may differ from E if for instance there are default%% values defined in <code>my_XML_Schema.xsd</code>.</p>process_validate(Schema,Xml,Opts) -> TargetNamespace = target_namespace(Xml), case Schema of [H|_] when is_list(H); is_tuple(H) -> case process_schemas(Schema, [{target_namespace,TargetNamespace}|Opts]) of {ok,S} -> S2 = validation_options(S,Opts), validate3(S2#xsd_state.schema_name,Xml,S2); Err -> Err end; _ -> process_validate2(xmerl_scan:file(Schema),Schema,Xml,Opts) end.process_validate2(Err={error,_},_,_,_) -> Err;process_validate2({SE,_},Schema,Xml,Opts) -> S = initiate_state(Opts,Schema), S1 = validate_schema(SE,S), S2 = validate_schema_ph2(S1), S3 = schema_concistence_checks(S2), S4 = validation_options(S3,Opts), validate3(Schema,Xml,S4).validate3(Schema,Xml,S=#xsd_state{errors=[]}) -> Ret = {_,S2} = case catch validate_xml(Xml,S) of {[XML2],[],Sx} -> {XML2,Sx}; {XML2,[],Sx} -> {XML2,Sx}; {_,UnValidated,Sx} -> {Xml,acc_errs(Sx,{error_path(UnValidated,Xml#xmlElement.name),?MODULE,{unvalidated_rest,UnValidated}})}; _Err = {error,Reason} -> {Xml,acc_errs(S,Reason)}; {'EXIT',Reason} -> {Xml,acc_errs(S,{error_path(Xml,Xml#xmlElement.name),?MODULE,{undefined,{internal_error,Reason}}})} end, save_to_file(S2,filename:rootname(Schema)++".tab2"), case S2#xsd_state.errors of [] -> Ret; L -> delete_table(S2), return_error(L) end;validate3(_,_,S) -> return_schema_error(S#xsd_state.errors).%% @spec process_schema(Schema) -> Result%% @equiv process_schema(Schema,[])process_schema(Schema) -> process_schema(Schema,[]).%% @spec process_schema(Schema,Options) -> Result%% Schema = filename()%% Result = {ok,State} | {error,Reason}%% State = global_state()%% Reason = [ErrorReason] | ErrorReason%% Options = option_list()%% @doc Reads the referenced XML schema and controls it is valid.%% Returns the <code>global_state()</code> with schema info or an %% error reason. The error reason may be a list of several errors%% or a single error encountered during the processing.process_schema(Schema,Options) when is_list(Options) -> S = initiate_state(Options,Schema), process_schema2(xmerl_scan:file(Schema),S,Schema);process_schema(Schema,State) when is_record(State,xsd_state) -> process_schema2(xmerl_scan:file(Schema),State,Schema).process_schema2(Err={error,_},_,_) -> Err;process_schema2({SE,_},State,_Schema) -> S1 = validate_schema(SE,State), S2 = validate_schema_ph2(S1), case schema_concistence_checks(S2) of S3 = #xsd_state{errors=[]} -> {ok,S3}; S3 -> delete_table(S3), return_error(S3#xsd_state.errors) end.%% @spec process_schemas(Schemas) -> Result%% @equiv process_schema(Schemas,[])process_schemas(Schemas) -> process_schemas(Schemas,[]).%% @spec process_schemas(Schemas,Options) -> Result%% Schemas = [{NameSpace,filename()}|Schemas] | []%% Result = {ok,State} | {error,Reason}%% Reason = [ErrorReason] | ErrorReason%% Options = option_list()%% @doc Reads the referenced XML schemas and controls they are valid.%% Returns the <code>global_state()</code> with schema info or an %% error reason. The error reason may be a list of several errors%% or a single error encountered during the processing.process_schemas(Schemas=[{_,Schema}|_],Options) when is_list(Options) -> process_schemas(Schemas,initiate_state(Options,Schema));process_schemas([{_NS,Schema}|Rest],State=#xsd_state{fetch_fun=Fetch}) ->%% case process_external_schema_once(Schema,if_list_to_atom(NS),State) of%% S when is_record(S,xsd_state) ->%% case process_schema(filename:join([State#xsd_state.xsd_base,Schema]),State) of%% {ok,S} -> Res= case Fetch(Schema,State) of {ok,{file,File},_} -> process_schema2(xmerl_scan:file(File),State,Schema); {ok,{string,Str},_} -> process_schema2(xmerl_scan:string(Str),State,Schema); {ok,[],_} -> {ok,State}; Err -> Err end, case Res of {ok,S2} -> process_schemas(Rest,S2); _ -> Res end;process_schemas([],S) when is_record(S,xsd_state) -> {ok,S}.initiate_state(Opts,Schema) -> XSDBase = filename:dirname(Schema), {{state,S},RestOpts}=new_state(Opts), S2 = create_tables(S), initiate_state2(S2#xsd_state{schema_name = Schema, xsd_base = XSDBase, fetch_fun = fun fetch/2},RestOpts).initiate_state2(S,[]) -> S;initiate_state2(S,[{tab2file,Bool}|T]) -> initiate_state2(S#xsd_state{tab2file=Bool},T);initiate_state2(S,[{xsdbase,XSDBase}|T]) -> initiate_state2(S#xsd_state{xsd_base=XSDBase},T);initiate_state2(S,[{fetch_fun,FetchFun}|T]) -> initiate_state2(S#xsd_state{fetch_fun=FetchFun},T);initiate_state2(S,[{fetch_path,FetchPath}|T]) -> initiate_state2(S#xsd_state{fetch_path=FetchPath},T);initiate_state2(S,[{schema_preprocessed,Bool}|T]) -> initiate_state2(S#xsd_state{schema_preprocessed=Bool},T);initiate_state2(S,[{target_namespace,_NS}|T]) ->%% initiate_state2(S#xsd_state{targetNamespace=if_list_to_atom(NS)},T); initiate_state2(S,T); %% used in validation phaseinitiate_state2(S,[H|T]) -> error_msg("Invalid option: ~p~n",[H]), initiate_state2(S,T).validation_options(S,[{target_namespace,NS}|T]) -> validation_options(S#xsd_state{targetNamespace=if_list_to_atom(NS)},T);validation_options(S,[_H|T]) -> validation_options(S,T);validation_options(S,[]) -> S.new_state(Opts) -> XSD_VSN = xmerl_xsd_vsn(), keysearch_delete(state,1,Opts,{state,#xsd_state{vsn=XSD_VSN}}).%% validate_schema/2 traverses the shema element to save necessary%% information as defined elements and types.validate_schema(E=#xmlElement{}, S) -> %% namespace is always a xmlNamespace record, attributs a list of %% #xmlAttributes and content a list of #xmlElements|#xmlText|... %% Have to save namespace nodes. Use of namespace in paths for %% unique,key and keyref are used after the schema is processed. S1 = S#xsd_state{targetNamespace=target_namespace(E)}, case is_already_processed(S1#xsd_state.targetNamespace,S1) of true -> save_namespace_definition(S1#xsd_state.targetNamespace,S1); _ -> S2 = S1,%save_namespace_definition(S1#xsd_state.targetNamespace,S1), {CM,S3} = traverse_content(E,S2),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -