From ddce778a59f866570714f7acb42756293151227a Mon Sep 17 00:00:00 2001 From: Willem Date: Thu, 4 Feb 2016 09:42:06 +0100 Subject: [PATCH] added 'strict' feature: more type checks and conversions. --- examples/erlsom_example/erlsom_example.erl | 6 ++- examples/soap_example/soap_example.erl | 6 +-- src/erlsom.erl | 2 +- src/erlsom_compile.erl | 27 ++++++------- src/erlsom_example_value.erl | 28 ++++++++++++++ src/erlsom_lib.erl | 3 +- src/erlsom_parseXsd.erl | 1 + src/erlsom_type2xsd.erl | 44 +++++++++++++++------- src/erlsom_write.erl | 2 +- 9 files changed, 84 insertions(+), 35 deletions(-) diff --git a/examples/erlsom_example/erlsom_example.erl b/examples/erlsom_example/erlsom_example.erl index 0250ce9..113d7a8 100644 --- a/examples/erlsom_example/erlsom_example.erl +++ b/examples/erlsom_example/erlsom_example.erl @@ -14,8 +14,10 @@ run() -> %% compile xsd - {ok, ModelIn} = erlsom:compile_xsd_file(example_in_xsd(), [{prefix, "in"}]), - {ok, ModelOut} = erlsom:compile_xsd_file(example_out_xsd(),[{prefix, "out"}]), + {ok, ModelIn} = erlsom:compile_xsd_file(example_in_xsd(), [{prefix, "in"}, + {strict, false}]), + {ok, ModelOut} = erlsom:compile_xsd_file(example_out_xsd(),[{prefix, "out"}, + {strict, false}]), %% parse xml {ok, Input, _} = erlsom:scan_file(example_in_xml(), ModelIn), diff --git a/examples/soap_example/soap_example.erl b/examples/soap_example/soap_example.erl index 9c9226d..13cb54d 100644 --- a/examples/soap_example/soap_example.erl +++ b/examples/soap_example/soap_example.erl @@ -67,9 +67,9 @@ compileXSDs() -> EnvelopeXsd = filename:join([codeDir(), "soap-envelope.xsd"]), BodyXsd = filename:join([codeDir(), "example_in.xsd"]), ResultXsd = filename:join([codeDir(), "example_out.xsd"]), - {ok, SoapModel} = erlsom:compile_xsd_file(EnvelopeXsd, [{prefix, "sp"}]), - {ok, ModelIn} = erlsom:add_xsd_file(BodyXsd, [{prefix, "in"}], SoapModel), - {ok, ModelOut} = erlsom:add_xsd_file(ResultXsd, [{prefix, "out"}], SoapModel), + {ok, SoapModel} = erlsom:compile_xsd_file(EnvelopeXsd, [{prefix, "sp"}, {strict, false}]), + {ok, ModelIn} = erlsom:add_xsd_file(BodyXsd, [{prefix, "in"}, {strict, false}], SoapModel), + {ok, ModelOut} = erlsom:add_xsd_file(ResultXsd, [{prefix, "out"}, {strict, false}], SoapModel), {ModelIn, ModelOut}. calcAverage(List, Precision) -> diff --git a/src/erlsom.erl b/src/erlsom.erl index 4c94147..6438329 100644 --- a/src/erlsom.erl +++ b/src/erlsom.erl @@ -111,7 +111,7 @@ %% - qname - converted to and from a #qname{} record %% All other types are treated as strings. %% -%% If strict is true (this is the default), additionally the +%% If strict is true, additionally the %% following types are checked and converted: %% - positiveInteger, ..TODO - all translated to integer %% - float - translated to/from a float or the atoms 'NaN', diff --git a/src/erlsom_compile.erl b/src/erlsom_compile.erl index d97d5ff..129cef3 100644 --- a/src/erlsom_compile.erl +++ b/src/erlsom_compile.erl @@ -72,7 +72,7 @@ afd, %% attribute form default nsp, %% namespacePrefix nss, %% namespaces ([#namespace{}]) - strict = true :: boolean(), %% additional type checking/conversion + strict :: boolean(), %% additional type checking/conversion includeFun, %% function to find included XSDs includeDirs, %% directories to look for XSDs includeFiles, %% tuples {Namespace, Prefix, Location} @@ -172,7 +172,7 @@ compile_internal(Xsd, Options, Parsed) -> _ -> [] end, - Strict = proplists:get_value(strict, Options, true), + Strict = proplists:get_value(strict, Options, false), %% the 'already_imported' option is necessary in case the imports %% are in separate XSDs that refer to each other. This happens %% for example in the Salesforce WSDL (in a circular way). @@ -363,7 +363,8 @@ processImports([Impt = #includeType{} | Tail], Acc = #p1acc{includeDirs = Dirs, processImports(Tail, Acc4); processImports([#redefineType{schemaLocation = Location, elements = Redefines} | Tail], - Acc = #p1acc{includeDirs = Dirs, includeFun = InclFun, nsp = Prefix, nss = Namespaces}) -> + Acc = #p1acc{includeDirs = Dirs, includeFun = InclFun, nsp = Prefix, + nss = Namespaces, strict = Strict}) -> {Xsd, _Prefix} = InclFun(undefined, Location, [], @@ -375,7 +376,7 @@ processImports([#redefineType{schemaLocation = Location, Acc3 = (#p1acc{tps = ImportedTypes} = transform(ParsedGrammar, Acc2)), #p1acc{tps = TypesToRedefine} = transformTypes(Redefines, Acc2), %% TODO : deal with attribute groups - Types2 = replaceElements(ImportedTypes, ImportedTypes, TypesToRedefine, Namespaces), + Types2 = replaceElements(ImportedTypes, ImportedTypes, TypesToRedefine, Namespaces, Strict), Acc4 = Acc#p1acc{tps = Acc#p1acc.tps ++ Types2, nsp = Prefix, seqCnt = Acc#p1acc.seqCnt, @@ -384,18 +385,18 @@ processImports([#redefineType{schemaLocation = Location, %% debugTypes(Acc4#p1acc.tps), processImports(Tail, Acc4). -replaceElements(ImportedTypes, Types, TypesToRedefine, Namespaces) -> +replaceElements(ImportedTypes, Types, TypesToRedefine, Namespaces, Strict) -> %% returns the imported types with redefinitions applied - replaceElements(ImportedTypes, Types, TypesToRedefine, [], Namespaces). + replaceElements(ImportedTypes, Types, TypesToRedefine, [], Namespaces, Strict). -replaceElements(_ImportedTypes, [], _Elements, Acc, _Namespaces) -> +replaceElements(_ImportedTypes, [], _Elements, Acc, _Namespaces, _Strict) -> Acc; replaceElements(ImportedTypes, [Original = #typeInfo{typeName= Name, typeType = TypeT} | Tail], - TypesToRedefine, #p1acc{strict=Strict} = Acc, Namespaces) + TypesToRedefine, Acc, Namespaces, Strict) when TypeT /= globalElementRefOnly -> case lists:keysearch(Name, #typeInfo.typeName, TypesToRedefine) of {value, RedefinedType = #typeInfo{extends = undefined}} -> - replaceElements(ImportedTypes, Tail, TypesToRedefine, [RedefinedType | Acc], Namespaces); + replaceElements(ImportedTypes, Tail, TypesToRedefine, [RedefinedType | Acc], Namespaces, Strict); {value, Redefined = #typeInfo{base = Base, anyAttr = AnyAttr, elements = Elemts, attributes = Attrs}} -> RedefinedType = case erlsom_lib:searchBase(erlsom_lib:makeTypeRef(Base, Namespaces, Strict), ImportedTypes) of @@ -410,13 +411,13 @@ replaceElements(ImportedTypes, [Original = #typeInfo{typeName= Name, typeType = _Else -> throw({error, "Base type not found: " ++ erlsom_lib:makeTypeRef(Base, Namespaces, Strict)}) end, - replaceElements(ImportedTypes, Tail, TypesToRedefine, [RedefinedType | Acc], Namespaces); + replaceElements(ImportedTypes, Tail, TypesToRedefine, [RedefinedType | Acc], Namespaces, Strict); _ -> - replaceElements(ImportedTypes, Tail, TypesToRedefine, [Original | Acc], Namespaces) + replaceElements(ImportedTypes, Tail, TypesToRedefine, [Original | Acc], Namespaces, Strict) end; replaceElements(ImportedTypes, [Original = #typeInfo{} | Tail], - TypesToRedefine, Acc, Namespaces) -> - replaceElements(ImportedTypes, Tail, TypesToRedefine, [Original | Acc], Namespaces). + TypesToRedefine, Acc, Namespaces, Strict) -> + replaceElements(ImportedTypes, Tail, TypesToRedefine, [Original | Acc], Namespaces, Strict). %% Deals with targetNamespace and 'elementFormDefault' setting. %% information from this schema is combined with info from a potential 'parent' XSD. diff --git a/src/erlsom_example_value.erl b/src/erlsom_example_value.erl index 7b86c1d..5ee654d 100644 --- a/src/erlsom_example_value.erl +++ b/src/erlsom_example_value.erl @@ -307,8 +307,36 @@ default_value(bool, _, _) -> "true"; default_value(any, _, _) -> "undefined"; +default_value(qname, _, _) -> + "qname"; default_value(integer, _, _) -> "42"; +default_value({integer, long}, _, _) -> + "42000"; +default_value({integer, int}, _, _) -> + "4200"; +default_value({integer, short}, _, _) -> + "420"; +default_value({integer, byte}, _, _) -> + "42"; +default_value({integer, unsignedLong}, _, _) -> + "43000"; +default_value({integer, unsignedInt}, _, _) -> + "4300"; +default_value({integer, unsignedShort}, _, _) -> + "430"; +default_value({integer, unsignedByte}, _, _) -> + "43"; +default_value({integer, nonPositiveInteger}, _, _) -> + "0"; +default_value({integer, positiveInteger}, _, _) -> + "42"; +default_value({integer, negativeInteger}, _, _) -> + "-42"; +default_value({integer, nonNegativeInteger}, _, _) -> + "0"; +default_value(float, _, _) -> + "3.1415927"; default_value(char, _, _) -> "\"?\""; default_value(Type, Model, State) -> diff --git a/src/erlsom_lib.erl b/src/erlsom_lib.erl index 38eedf0..6ddc35a 100644 --- a/src/erlsom_lib.erl +++ b/src/erlsom_lib.erl @@ -328,7 +328,6 @@ translateType(String, true) -> float; % TODO: add things like PositiveInteger here _Else -> - io:format("Else: ~p~n", [_Else]), 'char' end. @@ -1205,7 +1204,7 @@ xmlString(String) when is_binary(String) -> xmlString(String2). convert_integer({_, Subtype}, Value) -> - check_int(Subtype, list_to_integer(Value)). + check_int(Subtype, list_to_integer(string:strip(Value))). check_int(nonNegativeInteger, V) when V >= 0 -> V; diff --git a/src/erlsom_parseXsd.erl b/src/erlsom_parseXsd.erl index 3cff373..6a9b72a 100644 --- a/src/erlsom_parseXsd.erl +++ b/src/erlsom_parseXsd.erl @@ -43,6 +43,7 @@ xsdModel(Namespaces) -> #type{nm = '_document', els = [#el{alts = [#alt{tag = 'xsd:schema', tp = 'schemaType'}, #alt{tag = 'xsd:element', tp = 'globalElementType'}, + #alt{tag = 'xsd:import', tp = 'importType'}, #alt{tag = 'xsd:complexType', tp = 'globalComplexTypeType'}, #alt{tag = 'xsd:simpleType', tp = 'globalSimpleTypeType'}, #alt{tag = 'xsd:attribute', tp = 'globalAttributeType'}, diff --git a/src/erlsom_type2xsd.erl b/src/erlsom_type2xsd.erl index 11bc83e..688996f 100644 --- a/src/erlsom_type2xsd.erl +++ b/src/erlsom_type2xsd.erl @@ -169,7 +169,7 @@ translateElement(FieldName, Type, #state{ns = Tns}) -> translateAttribute(Field, Type, Tns) -> %% TODO: a check on the validity of attribute types - {TranslatedType, MinOccurs, MaxOccurs} = translateType(Type, Tns), + {TranslatedType, _MinOccurs, _MaxOccurs} = translateType(Type, Tns), %% TODO: attributes can be optional #localAttributeType{name = Field, type = TranslatedType}. @@ -209,37 +209,55 @@ translateType({type, _, union, Alternatives}, Tns) -> end, case Alternatives2 of [{type, _, SimpleType, _} = TheType] when SimpleType == integer; SimpleType == boolean; - SimpleType == string; SimpleType == record -> %% not really a choice + SimpleType == string; SimpleType == record; + SimpleType == float; SimpleType == non_neg_integer; + SimpleType == pos_integer; + SimpleType == neg_integer -> + %% not really a choice {Type, _, MaxOccurs} = translateType(TheType, Tns), {Type, MinOccurs, MaxOccurs}; + %% some special cases that correspond to types that are generated by + %% erlsom:write_xsd_hrl_file for the types float and + %% nonPositiveInteger(): + [{type,_,float,[]}, {atom,_,'NaN'}, {atom,_,'INF'}, {atom,_,'-INF'}] -> + Type = #qname{localPart = "float", + uri = "http://www.w3.org/2001/XMLSchema"}, + {Type, MinOccurs, undefined}; + [{type,_,neg_integer,[]},{integer,_,0}] -> + Type = #qname{localPart = "nonPositiveInteger", + uri = "http://www.w3.org/2001/XMLSchema"}, + {Type, MinOccurs, undefined}; [{type, _, list, [Element]}] -> %% not really a choice {Type, _, _} = translateType(Element, Tns), {Type, MinOccurs, "unbounded"}; _ -> - TranslatedAlternatives = [translateAlternative(Alternative, Tns) || Alternative <- Alternatives2], + TranslatedAlternatives = [translateAlternative(Alternative, Tns) || + Alternative <- Alternatives2], {#choiceType{alternatives = TranslatedAlternatives}, MinOccurs, undefined} end; translateType({type, _, list, [Element]}, Tns) -> TranslatedElement = translateType(Element, Tns), {TranslatedElement, "0", "unbounded"}; - translateType({type, _, record, [{atom, _, RecordType}]}, Tns) -> {qname(atom_to_list(RecordType), Tns), undefined, undefined}; - translateType({atom, _, undefined}, _) -> undefined; -translateType({type, _, integer, []}, _) -> - {#qname{localPart = "integer", uri = "http://www.w3.org/2001/XMLSchema"}, - undefined, undefined}; -translateType({type, _, boolean, []}, _) -> - {#qname{localPart = "boolean", uri = "http://www.w3.org/2001/XMLSchema"}, - undefined, undefined}; -translateType({type, _, string, []}, _) -> - {#qname{localPart = "string", uri = "http://www.w3.org/2001/XMLSchema"}, +translateType({type, _, Base_type, []}, _) -> + {#qname{localPart = translate_base_type(Base_type), + uri = "http://www.w3.org/2001/XMLSchema"}, undefined, undefined}. +translate_base_type(integer) -> "integer"; +translate_base_type(float) -> "float"; +translate_base_type(boolean) -> "boolean"; +translate_base_type(string) -> "string"; +translate_base_type(pos_integer) -> "positiveInteger"; +translate_base_type(non_neg_integer) -> "nonNegativeInteger"; +translate_base_type(neg_integer) -> "negativeInteger". + + %% alternatives have to be references to records (or lists of those). translateAlternative({type, _, record, [{atom, _, RecordName}]}, Tns) -> #localElementType{name = atom_to_list(RecordName), type = qname(atom_to_list(RecordName), Tns)}. diff --git a/src/erlsom_write.erl b/src/erlsom_write.erl index d31ac9d..938fde6 100644 --- a/src/erlsom_write.erl +++ b/src/erlsom_write.erl @@ -753,7 +753,7 @@ printElement(TextValue, Tag, RealElement, Namespaces, DeclaredNamespaces, QnameN %% new declared namespaces (since those would apply only to child-elements, of %% which there are none) {NamespacesString, _, Extra_prefix} = processNamespaces(TagAsText, Namespaces, DeclaredNamespaces), - [$<, Extra_prefix, TagAsText, NamespacesString, QnameNs, $>, TextValue, "]; + [$<, Extra_prefix, TagAsText, NamespacesString, QnameNs, $>, TextValue, "]; true -> TextValue end.