Skip to content

Commit

Permalink
added 'strict' feature: more type checks and conversions.
Browse files Browse the repository at this point in the history
  • Loading branch information
willemdj committed Feb 4, 2016
1 parent 9f65d1c commit ddce778
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 35 deletions.
6 changes: 4 additions & 2 deletions examples/erlsom_example/erlsom_example.erl
Expand Up @@ -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),
Expand Down
6 changes: 3 additions & 3 deletions examples/soap_example/soap_example.erl
Expand Up @@ -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) ->
Expand Down
2 changes: 1 addition & 1 deletion src/erlsom.erl
Expand Up @@ -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',
Expand Down
27 changes: 14 additions & 13 deletions src/erlsom_compile.erl
Expand Up @@ -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}
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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,
[],
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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.
Expand Down
28 changes: 28 additions & 0 deletions src/erlsom_example_value.erl
Expand Up @@ -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) ->
Expand Down
3 changes: 1 addition & 2 deletions src/erlsom_lib.erl
Expand Up @@ -328,7 +328,6 @@ translateType(String, true) ->
float;
% TODO: add things like PositiveInteger here
_Else ->
io:format("Else: ~p~n", [_Else]),
'char'
end.

Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/erlsom_parseXsd.erl
Expand Up @@ -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'},
Expand Down
44 changes: 31 additions & 13 deletions src/erlsom_type2xsd.erl
Expand Up @@ -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}.

Expand Down Expand Up @@ -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)}.
Expand Down
2 changes: 1 addition & 1 deletion src/erlsom_write.erl
Expand Up @@ -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, "</", TagAsText, $>];
[$<, Extra_prefix, TagAsText, NamespacesString, QnameNs, $>, TextValue, "</", Extra_prefix, TagAsText, $>];
true ->
TextValue
end.
Expand Down

0 comments on commit ddce778

Please sign in to comment.