Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:18
erlsom
erlsom-1.3.1-git.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File erlsom-1.3.1-git.patch of Package erlsom
diff --git a/src/erlsom.erl b/src/erlsom.erl index 8a625b0..f3e1a59 100644 --- a/src/erlsom.erl +++ b/src/erlsom.erl @@ -46,6 +46,9 @@ -include("erlsom.hrl"). -include("erlsom_parse.hrl"). +-opaque model() :: #model{}. +-export_type([model/0]). + %%---------------------------------------------------------------------- %% Function: compile_xsd/2 diff --git a/src/erlsom_compile.erl b/src/erlsom_compile.erl index 5e78669..7893451 100644 --- a/src/erlsom_compile.erl +++ b/src/erlsom_compile.erl @@ -74,7 +74,7 @@ includeFun, %% function to find included XSDs includeDirs, %% directories to look for XSDs includeFiles, %% tuples {Namespace, Prefix, Location} - %% or {Namespace, Schema, Prefix}, where + %% or {Namespace, Prefix, Schema}, where %% Schema is a parsed XSD (used to parse WSDLs %% with multiple XSDs). imported = [], %% a list of imported namespaces, to prevent @@ -169,6 +169,12 @@ compile_internal(Xsd, Options, Parsed) -> {value, {_, Files}} -> Files; _ -> Namespaces %% the two options are mutually exclusive end, + %% 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. + %% Since we still need to know the prefix, this is a list + %% [{Uri, Prefix}]. + AlreadyImported = proplists:get_value('already_imported', Options, []), put(erlsom_typePrefix, TypePrefix), put(erlsom_groupPrefix, GroupPrefix), put(erlsom_elementPrefix, ElementPrefix), @@ -182,7 +188,7 @@ compile_internal(Xsd, Options, Parsed) -> case ParsingResult of {error, _Message} -> ParsingResult; ParsedXsd -> compile_parsed_xsd(ParsedXsd, Prefix, Ns, IncludeFun, IncludeDirs, IncludeFiles, - IncludeAnyAttribs, ValueFun) + IncludeAnyAttribs, ValueFun, AlreadyImported) end. defaultValueFun() -> @@ -191,10 +197,10 @@ defaultValueFun() -> %% obsolete compile_parsed_xsd(ParsedXsd, Prefix, Namespaces) -> - compile_parsed_xsd(ParsedXsd, Prefix, Namespaces, fun erlsom_lib:findFile/4, ["."], [], true, defaultValueFun()). + compile_parsed_xsd(ParsedXsd, Prefix, Namespaces, fun erlsom_lib:findFile/4, ["."], [], true, defaultValueFun(), []). compile_parsed_xsd(ParsedXsd, Prefix, Namespaces, IncludeFun, IncludeDirs, IncludeFiles, - IncludeAnyAttribs, ValueFun) -> + IncludeAnyAttribs, ValueFun, AlreadyImported) -> %% InfoRecord will contain some information required along the way TargetNs = ParsedXsd#schemaType.targetNamespace, case Prefix of @@ -215,10 +221,16 @@ compile_parsed_xsd(ParsedXsd, Prefix, Namespaces, IncludeFun, IncludeDirs, Inclu Nsp = Pf1, Nss = [#ns{prefix = Pf2, uri = TargetNs} | Namespaces]; #ns{prefix = Pf} -> - Nsp = Pf ++ ":", + case Pf of + undefined -> Nsp = ""; + _ -> Nsp = Pf ++ ":" + end, Nss = Namespaces end end, + ImportedNs = [Uri || {Uri, _} <- AlreadyImported], + ImportedNsMapping = [#ns{prefix = P, uri = U} || {U, P} <- AlreadyImported], + ToBeImportedNsMapping = [#ns{prefix = P, uri = U} || {U, P, _} <- IncludeFiles], Acc = #p1acc{tns = TargetNs, includeFun = IncludeFun, includeDirs = IncludeDirs, @@ -226,7 +238,8 @@ compile_parsed_xsd(ParsedXsd, Prefix, Namespaces, IncludeFun, IncludeDirs, Inclu efd = ParsedXsd#schemaType.elementFormDefault, afd = ParsedXsd#schemaType.attributeFormDefault, nsp = Nsp, - nss = Nss}, + nss = Nss ++ ToBeImportedNsMapping ++ ImportedNsMapping, + imported = ImportedNs}, case catch transform(ParsedXsd, Acc) of {error, Message} -> {error, Message}; {'EXIT', Message} -> throw({'EXIT', Message}); @@ -471,20 +484,28 @@ transformTypes([#globalElementType{name=Name, type=Type, simpleOrComplex=undefin transformTypes([#globalComplexTypeType{name=Name, model=Model, attributes=Attributes, anyAttribute=AnyAttr, mixed = Mixed} | Tail], Acc = #p1acc{nsp = Prefix, tns = Tns}) -> - Path = Name ++ "/", - {Type, Acc2} = translateComplexTypeModel(Model, Acc#p1acc{path = Path}), - TheAttributes = Type#typeInfo.attributes ++ translateAttributes(Attributes, Acc2), - AnyAttrValue = if - AnyAttr /= undefined -> translateAnyAttrValue(AnyAttr, Tns); - true -> Type#typeInfo.anyAttr - end, - ResultSoFar = Acc2#p1acc.tps, - Type2 = Type#typeInfo{typeName= erlsom_lib:makeTypeName(Name, Prefix), global = true, - attributes=TheAttributes, - anyAttr = AnyAttrValue, - mixed = if Mixed -> Mixed; true -> Type#typeInfo.mixed end, - typeType = type}, - transformTypes(Tail, Acc2#p1acc{path = [], tps = [Type2 | ResultSoFar]}); + Path = Name ++ "/", + if + (Model == undefined) and (Mixed == true) -> + Element = #elementInfo{alternatives=[#alternative{tag="#text", type="##string", real=false}], min=0, max=1}, + Type = #typeInfo{elements = [Element], seqOrAll = text}, + Acc2 = Acc#p1acc{path = Path}; + true -> + {Type, Acc2} = translateComplexTypeModel(Model, Acc#p1acc{path = Path}) + end, + %{Type, Acc2} = translateComplexTypeModel(Model, Acc#p1acc{path = Path}), + TheAttributes = Type#typeInfo.attributes ++ translateAttributes(Attributes, Acc2), + AnyAttrValue = if + AnyAttr /= undefined -> translateAnyAttrValue(AnyAttr, Tns); + true -> Type#typeInfo.anyAttr + end, + ResultSoFar = Acc2#p1acc.tps, + Type2 = Type#typeInfo{typeName= erlsom_lib:makeTypeName(Name, Prefix), global = true, + attributes=TheAttributes, + anyAttr = AnyAttrValue, + mixed = if Mixed -> Mixed; true -> Type#typeInfo.mixed end, + typeType = type}, + transformTypes(Tail, Acc2#p1acc{path = [], tps = [Type2 | ResultSoFar]}); %%-record(globalSimpleTypeType, {name, annotation, model}). %% TODO: better would be to avoid getting the 'any' stuff in the @@ -709,7 +730,29 @@ translateAlternatives([Alternative | Tail], AlternativesSoFar, Acc) -> translateAlternatives(Tail, [TheAlternative | AlternativesSoFar], Acc2#p1acc{path = Path}); translateAlternatives([], AlternativesSoFar, Acc) -> - {lists:reverse(AlternativesSoFar), Acc}. + %% if there is an "any" alternative, then there is no point in listing + %% all the other alternatives. + case findAnyAlternative(AlternativesSoFar) of + {true, AnyAlternative} -> + {[AnyAlternative], Acc}; + false -> + {lists:reverse(AlternativesSoFar), Acc} + end. + +findAnyAlternative(Alternatives) -> + case lists:filter(fun isAny/1, Alternatives) of + [] -> + false; + [AnyAlternative |_] -> + {true, AnyAlternative} + end. + +%% for now only consider elements that are truely wildcards, because they seem +%% to be the most common type and they are the easiest to deal with. +isAny(#alt{tag = '#any', anyInfo = #anyInfo{prCont = "lax", ns = "##any"}}) -> + true; +isAny(_) -> + false. %% TODO: seqCnt should be passed from one element to the next, but not down into the %% tree (it should be reset to 0) (the first sequence-in-sequnce should always have nr. 1). @@ -878,5 +921,7 @@ translateQuasiAlternative(#localElementType{name=Name, type=Type, ref=undefined, %% translateNs(#ns{prefix = Prefix, uri = Ns}) -> %% {Ns, Prefix, undefined}. %% TODO: sort this out - what is correct? -translateNs(X) -> +translateNs({Uri, Prefix}) -> + #ns{uri = Uri, prefix = Prefix}; +translateNs(#ns{} = X) -> X. diff --git a/src/erlsom_lib.erl b/src/erlsom_lib.erl index 2eda411..2c2f128 100644 --- a/src/erlsom_lib.erl +++ b/src/erlsom_lib.erl @@ -44,11 +44,14 @@ newTree/0, addTreeElement/3, isAncestor/3, getAncestor/2, getLeaves/2, getDescendants/2, + documentAlternatives/1, emptyListIfUndefined/1, searchBase/2, makeQname/1, localName/1, + getUriFromQname/1, getTargetNamespaceFromXsd/1, - removePrefixes/1, unique/1]). + removePrefixes/1, unique/1, + getNamespacesFromModel/1, getPrefixFromModel/2]). -include("erlsom_compile.hrl"). -include("erlsom_sax.hrl"). @@ -930,10 +933,36 @@ makeQname(LocalName) -> localName(#qname{localPart = LocalName}) -> LocalName. +%%% hides the definition of #qname{} +getUriFromQname(#qname{uri = Uri}) -> + Uri. + %%% hides the definition of #schemaType{} getTargetNamespaceFromXsd(#schemaType{targetNamespace = TNS}) -> TNS. +%%% hides the definition of #model{} +getPrefixFromModel(#model{nss = Namespaces}, Uri) -> + case lists:keyfind(Uri, #ns.uri, Namespaces) of + false -> + ""; + #ns{prefix = Prefix} -> + Prefix + end. + +%%% hides the definition of #model{} +getNamespacesFromModel(#model{nss = Namespaces}) -> + [{Uri, Prefix} || #ns{prefix = Prefix, uri = Uri} <- Namespaces]. + + +%% these are the top-level elements. They can occur in an '#any' type. +%% Check on [], because if for some reason there would be no alternatives +%% there would be an endless loop. +documentAlternatives(#model{tps = [#type{nm = '_document', + els = [#el{alts = Alternatives}]} | + _]}) when Alternatives /= [] -> + Alternatives. + %% this is a hack, see erlsom_compile %% remove the type-prefix from the name removePrefixes(Name) -> diff --git a/src/erlsom_parse.erl b/src/erlsom_parse.erl index e730bdc..b8f90b0 100644 --- a/src/erlsom_parse.erl +++ b/src/erlsom_parse.erl @@ -170,6 +170,7 @@ -module(erlsom_parse). -export([xml2StructCallback/2]). -export([new_state/1]). +-export([new_state/2]). -include("erlsom.hrl"). -include("erlsom_sax.hrl"). -include("erlsom_parse.hrl"). %% the record definitions @@ -285,8 +286,11 @@ newRecord(#type{nm = Type, nr = NrOfElements}, false) -> %% to make it possible to use the callback without having %% knowledge about the #state{} and #model{} records. -new_state(#model{value_fun = ValFun} = Model) -> - #state{model=Model, namespaces=[], value_fun = ValFun}. +new_state(Model) -> + new_state(Model, []). +new_state(#model{value_fun = ValFun} = Model, Namespaces) -> + Nss = [#ns{prefix = Prefix, uri = Uri} || {Prefix, Uri} <- Namespaces], + #state{model=Model, namespaces=Nss, value_fun = ValFun}. %% This is the call-back function (called by the sax parser) %% Filters out some events and calls the state machine. @@ -744,10 +748,11 @@ stateMachine(Event, State = #state{currentState = #cs{re = RemainingElements, %% debugMessage("the work"), %% debugState(State), %% debugEvent(Event), - [#el{alts = Alternatives, - mn = MinOccurs, - mx = MaxOccurs, - nillable = Nullable} | NextElements] = RemainingElements, + [FirstElement | NextElements] = RemainingElements, + #el{alts = Alternatives, + mn = MinOccurs, + mx = MaxOccurs, + nillable = Nullable} = FirstElement, case Event of {startElement, Uri, LocalName, _Prefix, Attributes} -> Name = eventName(LocalName, Uri, NamespaceMapping), @@ -846,70 +851,80 @@ stateMachine(Event, State = #state{currentState = #cs{re = RemainingElements, _Else -> %% See whether there is an 'Any' alternative. - %% note: if processContents (prCont) = "strict", then that 'doesn't count' - %% because in that there should be a proper alternative. Arguably - %% the 'any' alternative should not be part of the model in that case, - %% but it is needed as an anchor when adding xsd's to the model. - %% case lists:keysearch(any, #alt.tp, Alternatives) of - case lists:keysearch('#any', #alt.tag, Alternatives) of - {value, #alt{nxt = NextTags, tp = Type, anyInfo = AnyInfo, rl = RealElement2}} - when AnyInfo#anyInfo.prCont /= "strict" -> - %% If the tag is in NextTags, we have to move on - %% debug(NextTags), - case lists:member(Name, NextTags) of - true -> - NewState = undefined; - false -> - if - MaxOccurs /= unbound, ReceivedSoFar >= MaxOccurs -> - %% can this be valid at all? - NewState = undefined; - true -> - %% push element - %% HERE - %% see whether this is a 'real' element. - if - RealElement2 -> - %% need to check on 'anyInfo' here!!!! - %% push the current status, create a new level in the state machine - %% case matchesAnyInfo(Uri, AnyInfo, Tns) of - case matchesAnyInfo(Uri, AnyInfo, Tns) of - true -> - NewState = State#state{currentState = #anyState{anyInfo = AnyInfo}, - resultSoFar = [State#state.currentState | ResultSoFar]}; - false -> - NewState = undefined - end; - true -> %% not a real element - TypeDef = findType(Type, Types, Attributes, TypeHierarchy, Namespaces, NamespaceMapping), - %% debug(Type), - #type{els = Elements, tp = Tp, mxd = Mxd} = TypeDef, - NewRecord = newRecord(TypeDef, StoreAnyAttr), - case Tp of - sequence -> - %% debug(NewRecord), - NewCurrentState = #cs{re = Elements, sf =0, er = NewRecord, rl = RealElement2, - mxd = Mxd}; - all -> - NewCurrentState = #all{re = Elements, er = NewRecord} - end, - %% push the current status, create a new level in the state machine - NextState = State#state{currentState = NewCurrentState, - resultSoFar = [State#state.currentState | ResultSoFar]}, - %% a helper-state. The tag we just received was NOT a start tag for a - %% corresponding enclosing element - there are no enclosing tags. The - %% tag just helped us to select the next type. Re-use the current event. - NewState = stateMachine(Event, NextState) - end - end - end; - - _NotAny -> - %% debug(lists:keysearch('#any', #alt.tag, Alternatives)), - %% debug("no Any alternative"), - %% debug(Alternatives), - NewState = undefined - end, + case Alternatives of + [#alt{tag='#any', anyInfo = #anyInfo{ns = AltNs}}] when AltNs /= "##other" -> + %% Add the elements of the model to the alternatives and try again + AnyAlternatives = Alternatives ++ erlsom_lib:documentAlternatives(State#state.model), + CurrentCurrentState = State#state.currentState, + RemainingElementsWithAny = [FirstElement#el{alts = AnyAlternatives} | NextElements], + CurrentStateWithAny = CurrentCurrentState#cs{re = RemainingElementsWithAny}, + NewState = stateMachine(Event, State#state{currentState = CurrentStateWithAny}); + _ -> + %% note: if processContents (prCont) = "strict", then that 'doesn't count' + %% because in that there should be a proper alternative. Arguably + %% the 'any' alternative should not be part of the model in that case, + %% but it is needed as an anchor when adding xsd's to the model. + case lists:keysearch('#any', #alt.tag, Alternatives) of %{ + {value, #alt{nxt = NextTags, tp = Type, anyInfo = AnyInfo, rl = RealElement2}} + when AnyInfo#anyInfo.prCont /= "strict" -> + %% If the tag is in NextTags, we have to move on + %% debug(NextTags), + case lists:member(Name, NextTags) of %{ + true -> + NewState = undefined; + false -> + if %{ + MaxOccurs /= unbound, ReceivedSoFar >= MaxOccurs -> + %% can this be valid at all? + NewState = undefined; + true -> + %% push element + %% HERE + %% see whether this is a 'real' element. + if %{ + RealElement2 -> + %% need to check on 'anyInfo' here!!!! + %% push the current status, create a new level in the state machine + %% case matchesAnyInfo(Uri, AnyInfo, Tns) of + case matchesAnyInfo(Uri, AnyInfo, Tns) of %{ + true -> + NewState = State#state{currentState = #anyState{anyInfo = AnyInfo}, + resultSoFar = [State#state.currentState | ResultSoFar]}; + false -> + NewState = undefined + end; %} + true -> %% not a real element + TypeDef = findType(Type, Types, Attributes, TypeHierarchy, Namespaces, NamespaceMapping), + %% debug(Type), + #type{els = Elements, tp = Tp, mxd = Mxd} = TypeDef, + NewRecord = newRecord(TypeDef, StoreAnyAttr), + case Tp of + sequence -> + %% debug(NewRecord), + NewCurrentState = #cs{re = Elements, sf =0, er = NewRecord, rl = RealElement2, + mxd = Mxd}; + all -> + NewCurrentState = #all{re = Elements, er = NewRecord} + end, + %% push the current status, create a new level in the state machine + NextState = State#state{currentState = NewCurrentState, + resultSoFar = [State#state.currentState | ResultSoFar]}, + %% a helper-state. The tag we just received was NOT a start tag for a + %% corresponding enclosing element - there are no enclosing tags. The + %% tag just helped us to select the next type. Re-use the current event. + NewState = stateMachine(Event, NextState) + end %} + end %} + end; %} + + _NotAny -> + %% debug(lists:keysearch('#any', #alt.tag, Alternatives)), + %% debug("no Any alternative"), + %% debug(Alternatives), + NewState = undefined + end %} + + end, %} if NewState == undefined -> diff --git a/src/erlsom_pass2.erl b/src/erlsom_pass2.erl index 24d68bb..ff102dd 100644 --- a/src/erlsom_pass2.erl +++ b/src/erlsom_pass2.erl @@ -706,16 +706,22 @@ pass5Alternative(Alternative = #alt{tag = Name, anyInfo = AnyInfo}, Types, NextE case Name of '#any' -> %% #anyInfo{prCont = Pc} = AnyInfo, - Pc = case AnyInfo of - #anyInfo{} -> AnyInfo#anyInfo.prCont; - undefined -> undefined - end, + {Ns, Pc} = case AnyInfo of + #anyInfo{} -> {AnyInfo#anyInfo.ns, AnyInfo#anyInfo.prCont}; + undefined -> {undefined, undefined} + end, case Pc of "lax" -> - [Alternative#alt{nxt=getNextTags(NextElements)} | getDocumentAlternatives(AnyInfo, Types, Info, Tns)]; - %% undefined -> - %% [Alternative#alt{nxt=getNextTags(NextElements), anyInfo = xxx}]; + case Ns of + "##other" -> + [Alternative#alt{nxt=getNextTags(NextElements)} | getDocumentAlternatives(AnyInfo, Types, Info, Tns)]; + _ -> + %% in this case we look for the top-level alternatives in the model when + %% required during the parsing. This to prevent the model from becoming very large + %% if there are a lot of "Any" extension points (like in the eBay WSDL, for example). + [Alternative#alt{nxt=getNextTags(NextElements)}] + end; _ -> [Alternative | getDocumentAlternatives(AnyInfo, Types, Info, Tns)] end; diff --git a/src/erlsom_write.erl b/src/erlsom_write.erl index f6fe541..7fcc60b 100644 --- a/src/erlsom_write.erl +++ b/src/erlsom_write.erl @@ -57,7 +57,7 @@ struct2xml(_Struct, [], ResultSoFar, _Model, _Namespaces, _Mixed) -> struct2xml(Struct, _StructModel = [ModelForThisElement = #el{alts = Alternatives, mn = Min, mx = Max, nr = SequenceNr, nillable = Nillable} | NextElements], - ResultSoFar, Model = #model{nss = Namespaces, th = Th}, DeclaredNamespaces, + ResultSoFar, Model = #model{nss = Namespaces}, DeclaredNamespaces, Mixed) -> %% Which alternative has been selected follows from the value of this element @@ -103,7 +103,7 @@ struct2xml(Struct, ResultForThisElement = printValue(CurrentValue, Alternatives, Namespaces, DeclaredNamespaces, Mixed); _ when is_tuple(V1) -> %% debug("alternative with MaxOccurs > 1"), - ResultForThisElement = processAlternatives(CurrentValue, Alternatives, Model, DeclaredNamespaces, Th, + ResultForThisElement = processAlternatives(CurrentValue, Alternatives, Model, DeclaredNamespaces, Mixed) end; #qname{} -> @@ -167,7 +167,7 @@ processElementValues([], %% - for a nillable element with attributes processElementValues([V1 | NextValues], ModelForThisElement = #el{alts = Alternatives, mx = Max, nillable=Nillable}, - ResultSoFar, Counter, Model = #model{nss = Namespaces, th = Th}, DeclaredNamespaces, Mixed) -> + ResultSoFar, Counter, Model = #model{nss = Namespaces}, DeclaredNamespaces, Mixed) -> %% debug("procesElementValues, counter = " ++ integer_to_list(Counter)), {Case, IncreaseCounter} = @@ -228,7 +228,7 @@ processElementValues([V1 | NextValues], ResultForThisElement = case Case of listOfTuples -> - processAlternatives(V1, Alternatives, Model, DeclaredNamespaces, Th, Mixed); + processAlternatives(V1, Alternatives, Model, DeclaredNamespaces, Mixed); mixed -> erlsom_lib:xmlString(V1); listOfStrings -> @@ -249,19 +249,19 @@ processElementValues([V1 | NextValues], Model, DeclaredNamespaces, Mixed). %% returns a string that represents the value -processSubType(Value, Alternatives, Model = #model{tps = Types, th = TypeHierarchy}, DeclaredNamespaces, +processSubType(Value, Alternatives, Model = #model{tps = Types}, DeclaredNamespaces, Mixed) -> %% RecordType can be an instantiated abstract type RecordType = element(1, Value), - {Alternative, Abstract} = findAlternative(RecordType, Alternatives, TypeHierarchy), + {Alternative, Abstract} = findAlternative(RecordType, Alternatives, Model), TypeRecord = findType(RecordType, Types), processAlternativeValue(Value, 1, Alternative, TypeRecord, Model, DeclaredNamespaces, Abstract, Mixed). -processAlternatives(Values = [Value | _], Alternatives, Model = #model{tps = Types}, DeclaredNamespaces, TypeHierarchy, +processAlternatives(Values = [Value | _], Alternatives, Model = #model{tps = Types}, DeclaredNamespaces, Mixed) -> %% See which alternative this is RecordType = element(1, Value), - {Alternative, Abstract} = findAlternative(RecordType, Alternatives, TypeHierarchy), + {Alternative, Abstract} = findAlternative(RecordType, Alternatives, Model), TypeRecord = findType(RecordType, Types), processAlternativeValues(Values, 0, Alternative, TypeRecord, Model, DeclaredNamespaces, Abstract, [], Mixed). @@ -345,19 +345,26 @@ findType(TypeName, Types) -> _ -> throw({error, "Something wrong with the Model"}) end. -findAlternative(RecordType, Alternatives, TypeHierarchy) -> - findAlternative(RecordType, Alternatives, TypeHierarchy, false). +findAlternative(RecordType, Alternatives, Model) -> + findAlternative(RecordType, Alternatives, Model, false). -findAlternative(RecordType, Alternatives, TypeHierarchy, Abstract) -> +findAlternative(RecordType, Alternatives, #model{th = TypeHierarchy} = Model, Abstract) -> case lists:keysearch(RecordType, #alt.tp, Alternatives) of {value, Alternative} -> {Alternative, Abstract}; _ -> - %% see whether an ancestor in the type hierarchy is among the alternatives - case erlsom_lib:getAncestor(RecordType, TypeHierarchy) of - {value, Ancestor} -> - findAlternative(Ancestor, Alternatives, TypeHierarchy, true); - _ -> - throw({error, "Struct doesn't match model: recordtype not expected: " ++ atom_to_list(RecordType)}) + %% see whether this is an '#any' type + case Alternatives of + [#alt{tag='#any', anyInfo = #anyInfo{ns = AltNs}}] when AltNs /= "##other" -> + AnyAlternatives = Alternatives ++ erlsom_lib:documentAlternatives(Model), + findAlternative(RecordType, AnyAlternatives, Model, Abstract); + _ -> + %% see whether an ancestor in the type hierarchy is among the alternatives + case erlsom_lib:getAncestor(RecordType, TypeHierarchy) of + {value, Ancestor} -> + findAlternative(Ancestor, Alternatives, Model, true); + _ -> + throw({error, "Struct doesn't match model: recordtype not expected: " ++ atom_to_list(RecordType)}) + end end end. diff --git a/src/erlsom_writeHrl.erl b/src/erlsom_writeHrl.erl index d834ae7..5319eb9 100644 --- a/src/erlsom_writeHrl.erl +++ b/src/erlsom_writeHrl.erl @@ -27,19 +27,28 @@ -module(erlsom_writeHrl). -export([writeHrl/1]). +-export([write_hrl/1]). -export([writeHrlFile/3]). -export([writeXsdHrlFile/2]). -include("erlsom_parse.hrl"). -include("erlsom.hrl"). +-type hrl_header() :: iolist(). %% only explanatory text (comment) +-type hrl_types() :: iolist(). %% the actual record and type declarations + %% debug(Text) -> io:format("writeHrl: ~p~n", [Text]). %% debug(Text1, Text2) -> %% io:format("~p ~p~n", [Text1, Text2]). -writeHrl(#model{tps = Types, th = TypeHierarchy, any_attribs = AnyAtts}) -> - [header(AnyAtts), writeTypes(Types, TypeHierarchy, AnyAtts)]. +-spec write_hrl(Model::erlsom:model()) -> {hrl_header(), hrl_types()}. +write_hrl(#model{tps = Types, th = TypeHierarchy, any_attribs = AnyAtts}) -> + {header(), writeTypes(Types, TypeHierarchy, AnyAtts)}. + +writeHrl(#model{} = Model) -> + {Header, Types} = write_hrl(Model), + [Header, Types]. writeHrlFile(Xsd, Prefix, Namespaces) -> %% compile file @@ -61,7 +70,7 @@ writeXsdHrlFile(Xsd, Options) -> throw({error, Error}) end. -header(AnyAtts) -> +header() -> "%% HRL file generated by ERLSOM\n" "%%\n" "%% It is possible (and in some cases necessary) to change the name of\n" @@ -69,7 +78,9 @@ header(AnyAtts) -> "%%\n" "%% It is possible to add default values, but be aware that these will\n" "%% only be used when *writing* an xml document.\n\n" - "\n" ++ + "\n". + +standard_types(AnyAtts) -> case AnyAtts of true -> "-type anyAttrib() :: {{string(), %% name of the attribute\n" @@ -88,26 +99,29 @@ header(AnyAtts) -> " mappedPrefix :: string()}).\n". writeTypes(Types, TypeHierarchy, AnyAtts) -> - [writeType(T, TypeHierarchy, AnyAtts) || T <- Types]. + [standard_types(AnyAtts), [writeType(T, TypeHierarchy, AnyAtts) || T <- Types]]. writeType(#type{nm = '_document'}, _, _) -> []; writeType(#type{nm = Name, els = Elements, atts = Attributes, mxd = Mixed}, Hierarchy, AnyAtts) -> - Format = - case AnyAtts of - true -> - "~3n-record(~p, {anyAttribs :: anyAttribs(),\n\t~s~s})." - "~2n-type ~s :: ~s."; - _ -> - "~3n-record(~p, {\n\t~s~s})." - "~2n-type ~s :: ~s." - end, - Args = [Name, writeAttributes(Attributes), - writeElements(Elements, Mixed, Hierarchy), + Format = "~3n-record(~p, {~s})." ++ + "~2n-type ~s :: ~s.", + Fields = [case AnyAtts of + true -> + "anyAttribs :: anyAttribs()"; + _ -> + [] + end, + writeAttributes(Attributes), + writeElements(Elements, Mixed, Hierarchy)], + Args = [Name, add_commas(Fields), formatType(Name), formatRecord(Name)], lists:flatten(io_lib:format(Format, Args)). +add_commas(Parts) -> + string:join(lists:filter(fun(S) -> S /= "" end, Parts), ","). + %% writeElements(Elements, Mixed, Hierarchy) -> %% writeElements(Elements, Mixed, Hierarchy, 0). @@ -125,7 +139,8 @@ writeElements(Elements, Mixed, Hierarchy) -> {[ElString | Acc], CountChoices2} end, {Result, _} = lists:foldl(WriteFun, {[], 0}, Elements), - string:join(lists:reverse(Result), ",\n\t"). + string:join(lists:reverse(Result), ","). + writeElement(#el{alts = Alternatives, mn = Min, mx = Max, nillable = Nillable}, Mixed, Hierarchy, CountChoices) -> {Label, Types, Count2} = case Mixed of @@ -136,9 +151,9 @@ writeElement(#el{alts = Alternatives, mn = Min, mx = Max, nillable = Nillable}, end, TypeSpec = case Mixed of true -> - [Label, "[", Types, " | string()]"]; + ["\n\t", Label, "[", Types, " | string()]"]; _ -> - [Label, Types] + ["\n\t", Label, Types] end, {lists:flatten(TypeSpec), Count2}. @@ -153,7 +168,14 @@ writeAlternatives(Alts, Min, Max, _N, Hierarchy, CountChoices) when length(Alts) _ -> ["choice", integer_to_list(CountChoices), " :: "] end, - Alternatives = [writeAlternative(A, 1, 1, false, Hierarchy) || A <- Alts], + Alternatives = case lists:keyfind('#any', #alt.tag, Alts) of + false -> + [writeAlternative(A, 1, 1, false, Hierarchy) || A <- Alts]; + Alt -> + %% it makes no sense to have a choice between many things if + %% one of them is "any()" - in that case the any() suffices. + [writeAlternative(Alt, 1, 1, false, Hierarchy)] + end, Type = lists:flatten([minMaxType(string:join(Alternatives, " | "), Min, Max, 1, false, simple)]), {Label, Type, CountChoices + 1}; @@ -162,7 +184,7 @@ writeAlternatives([#alt{tag = Tag, tp = Tp, rl=Rl} = Alt], Min, Max, Nillable, H LabelAtom = case Rl of true -> %% erlsom_lib:nameWithoutPrefix(atom_to_list(Tag)); - Tag; + baseName(Tag); _ when Rl == false; Rl == simple -> case Tp of {'#PCDATA', _} -> @@ -214,8 +236,18 @@ writeAttributes(Attributes) -> writeAttribute(#att{nm = Name, opt = Optional, tp = Type}) -> OptOrReq = if Optional -> " | undefined"; true -> "" end, Format = "~n\t~p :: ~s~s", - lists:flatten(io_lib:format(Format, [Name, makeType(Type), OptOrReq])). - + lists:flatten(io_lib:format(Format, [baseName(Name), makeType(Type), OptOrReq])). + +%% the names of the fields should not have the prefix +baseName(Atom) when is_atom(Atom) -> + String = atom_to_list(Atom), + String_no_prefix = case string:tokens(String, ":") of + [_Prefix, Name] -> + Name; + _ -> + String + end, + list_to_atom(String_no_prefix). formatSimpleType(Tp1, Tp2, Min, Max, Max2, Nullable) -> Type = simpleType(Tp1, Tp2),
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor