Current section
Files
Jump to
Current section
Files
src/fix_gen.erl
-module(fix_gen).
-include_lib("xmerl/include/xmerl.hrl").
-include_lib("fix_macros.hrl").
-import(xmerl_xs, [ xslapply/2, value_of/1, select/2, built_in_rules/2 ]).
-export([generate/1]).
generate(XMLDoc) ->
{XmlContent, _} = xmerl_scan:file(XMLDoc),
TagByName = tmp_parse_number_by_field(XmlContent),
?DBG("~p ~n",[TagByName]),
FieldsByMessage = tmp_parse_fields_by_message(XmlContent),
?DBG("~p ~n",[FieldsByMessage]),
Messages = [K || {K,_} <- FieldsByMessage],
HeaderFields = tmp_parse_header_fields(XmlContent),
?DBG("~p ~n", [HeaderFields]),
TrailerFields = tmp_parse_trailer_fields(XmlContent),
?DBG("~p ~n", [TrailerFields]),
case file:open("include/fix_messages.hrl", [write]) of
{ok, IOF} -> io:format(IOF, "~s~n", [tmp_record_header( XmlContent)]),
file:close(IOF);
{error, Reason} -> io:format("could not open file due to ~p.~n", Reason) end,
case file:open("src/fix_parser.erl", [write]) of
{ok, IOF2} -> io:format(IOF2, "~s~n", [tmp_message_parser_main_body(Messages)]),
io:format(IOF2, "~s~n", [tmp_message_parser(FieldsByMessage, TagByName,
HeaderFields, TrailerFields, [])]),
file:close(IOF2);
{error, Reason2} -> io:format("could not open file due to ~p.~n", Reason2) end.
tmp_parse_fields_by_message(E = #xmlElement{name = 'fix'}) ->
xslapply(fun tmp_parse_fields_by_message/1, select("messages/message", E));
tmp_parse_fields_by_message(E = #xmlElement{name = 'message'} ) ->
[Name] = xslapply(fun tmp_parse_fields_by_message/1, select("@name", E)),
[MsgType] = xslapply(fun tmp_parse_fields_by_message/1, select("@msgtype", E)),
ListOfFields = xslapply(fun tmp_parse_list_of_fields/1, select("field", E)),
{{string:to_lower(Name), MsgType}, ListOfFields};
tmp_parse_fields_by_message(E) ->
built_in_rules(fun tmp_parse_fields_by_message/1, E).
tmp_parse_number_by_field(E = #xmlElement{name='fix'}) ->
xslapply(fun tmp_parse_number_by_field/1, select("fields/field", E));
tmp_parse_number_by_field(E = #xmlElement{name = 'field'}) ->
[Tag] = xslapply(fun tmp_parse_number_by_field/1, select("@number", E)),
[Name] = xslapply(fun tmp_parse_number_by_field/1, select("@name", E)),
{string:to_lower(Name), Tag};
tmp_parse_number_by_field(E)->
built_in_rules(fun tmp_parse_number_by_field/1, E).
tmp_parse_list_of_fields(E = #xmlElement{name = 'field'}) ->
[Name] = xslapply(fun tmp_parse_list_of_fields/1, select("@name", E)),
string:to_lower(Name);
tmp_parse_list_of_fields(E)->
built_in_rules(fun tmp_parse_list_of_fields/1, E).
tmp_parse_header_fields(E = #xmlElement{name='fix'}) ->
xslapply(fun tmp_parse_header_fields/1, select("header/field", E));
tmp_parse_header_fields(E = #xmlElement{name='field'}) ->
[Name] = xslapply(fun tmp_parse_list_of_fields/1, select("@name", E)),
string:to_lower(Name);
tmp_parse_header_fields(E) ->
built_in_rules(fun tmp_parse_header_fields/1, E).
tmp_parse_trailer_fields(E = #xmlElement{name='fix'}) ->
xslapply(fun tmp_parse_trailer_fields/1, select("trailer/field", E));
tmp_parse_trailer_fields(E = #xmlElement{name='field'}) ->
[Name] = xslapply(fun tmp_parse_trailer_fields/1, select("@name", E)),
string:to_lower(Name);
tmp_parse_trailer_fields(E) ->
built_in_rules(fun tmp_parse_trailer_fields/1, E).
tmp_message_parser_main_body(Messages) ->
"-module(fix_parser).\n\n" ++
"-include_lib(\"fix_messages.hrl\").\n\n" ++
"-include_lib(\"fix_macros.hrl\").\n\n" ++
"-export([encode/1, decode/1]).\n\n" ++
"-define(SOH,[1]).\n\n" ++
"encode(Fix) ->\n" ++
"ok.\n\n" ++
"decode(MSG) when is_binary(MSG) ->\n" ++
" decode(binary_to_list(MSG));\n" ++
"decode(MSG) ->\n" ++
" Tokens = string:tokens(MSG, ?SOH),\n" ++
" Fun = fun(I) -> string:tokens(I, \"=\") end,\n" ++
" FieldList = lists:map(Fun, Tokens),\n" ++
" [[\"8\", BeginString], [\"9\", BodyLength], [\"35\", MsgType] | FieldList2] = FieldList,\n" ++
" case MsgType of\n" ++
tmp_message_parser_case(Messages,[]) ++
%%" \"0\" -> decodeHeartbeat(#heartbeat{header=#header{beginstring=BeginString, bodylength=BodyLength, msgtype = MsgType}, trailer=#trailer{}}, FieldList2);
" false -> false %% wrong format\n" ++
" end. \n".
%% \"0\" -> decodeHeartbeat(#heartbeat{header=#header{beginString=BeginString, bodyLength=BodyLength, msgType = MsgType}, trailer=#trailer{}}, FieldList2);
tmp_message_parser_case([] , Result)-> Result;
tmp_message_parser_case([{MsgName, MsgType} | REST], Result) ->
Result2 = " \"" ++ MsgType ++ "\" -> decode" ++ MsgName ++ "(#" ++ MsgName ++ "{header=#header{beginstring=BeginString, bodylength=BodyLength, msgtype = MsgType}, trailer=#trailer{}}, FieldList2) ;" ++ io_lib:nl() ++ Result,
tmp_message_parser_case(REST, Result2).
tmp_message_parser([],_, _, _,Result) -> [Result];
tmp_message_parser([{{MsgName,_},Fields} | REST ], TagByName, HeaderFields, TrailerFields, Result) ->
Result2 = Result ++ "decode" ++ MsgName ++ "(Msg,[]) -> \n" ++
" Msg;\n" ++
tmp_message_parser_header(HeaderFields, MsgName, TagByName,[]) ++
tmp_message_parser_trailer(TrailerFields,MsgName, TagByName, []) ++
tmp_message_parser_body(Fields, MsgName, TagByName, []) ++
"decode" ++ MsgName ++ "(Msg, [[_, Value] | Rest]) ->\n" ++
" decode" ++ MsgName ++ "(Msg,Rest).\n\n",
tmp_message_parser(REST, TagByName,HeaderFields,TrailerFields, Result2).
tmp_message_parser_header([], MsgName, _, Result) -> Result;
tmp_message_parser_header([FieldName | REST ],MsgName, TagByName, Result)->
case lists:keyfind(FieldName, 1, TagByName) of
{_,FieldTag} -> Result2 = "decode" ++ MsgName ++ "(Msg, [[\"" ++ FieldTag ++
"\", Value] | Rest]) -> \n" ++
" Header = Msg#" ++ MsgName ++ ".header#header{" ++ FieldName ++ "=Value},\n" ++
" decode" ++ MsgName ++ "(Msg#" ++ MsgName ++ "{header=Header},Rest);\n" ++ Result,
tmp_message_parser_header(REST,MsgName,TagByName, Result2);
false -> tmp_message_parser_header(REST,MsgName,TagByName, Result) end.
tmp_message_parser_trailer([], MsgName, _, Result)-> Result;
tmp_message_parser_trailer([FieldName | REST ],MsgName, TagByName, Result)->
case lists:keyfind(FieldName, 1, TagByName) of
{_,FieldTag} -> Result2 = "decode" ++ MsgName ++ "(Msg, [[\"" ++ FieldTag ++ "\", Value] | Rest]) -> \n" ++
" Trailer = Msg#" ++ MsgName ++ ".trailer#trailer{" ++ FieldName ++ "=Value},\n" ++
" decode" ++ MsgName ++ "(Msg#" ++ MsgName ++ "{trailer=Trailer},Rest);\n" ++ Result,
tmp_message_parser_trailer(REST,MsgName,TagByName, Result2);
false -> tmp_message_parser_trailer(REST,MsgName,TagByName, Result) end.
tmp_message_parser_body([], MsgName, _, Result)-> Result;
tmp_message_parser_body([FieldName | REST ],MsgName, TagByName, Result)->
case lists:keyfind(FieldName, 1, TagByName) of
{_,FieldTag} -> Result2 = "decode" ++ MsgName ++ "(Msg, [[\"" ++ FieldTag ++ "\", Value] | Rest]) -> \n" ++
" decode" ++ MsgName ++ "(Msg#" ++ MsgName ++ "{" ++ FieldName ++ "=Value}, Rest);\n" ++ Result,
tmp_message_parser_body(REST,MsgName,TagByName, Result2);
false -> tmp_message_parser_body(REST,MsgName,TagByName, Result) end.
tmp_record_header(E = #xmlElement{name='fix'}) ->
[xslapply(fun tmp_record_header/1, select("header", E)),
xslapply(fun tmp_record_header/1, select("trailer", E)),
xslapply(fun tmp_record_header/1, select("messages/message", E))];
tmp_record_header(E = #xmlElement{name='header'}) ->
case xslapply(fun tmp_record_header/1, select("field", E)) of
[FIRST | REST] -> ["-record(header,{", string:substr(FIRST,3), REST, "}).\n\n"];
[] -> [] end;
tmp_record_header(E = #xmlElement{name = 'trailer'}) ->
case xslapply(fun tmp_record_header/1, select("field", E)) of
[FIRST | REST] -> ["-record(trailer,{", string:substr(FIRST,3), REST, "}).\n\n"];
[] -> [] end;
tmp_record_header(E = #xmlElement{name='field'}) ->
[Value] = xslapply(fun tmp_record_header/1, select("@name", E)),
", " ++ string:to_lower(Value);
tmp_record_header(E = #xmlElement{name='message'}) ->
[Name] = xslapply(fun tmp_record_header/1, select("@name", E)),
"-record(" ++ string:to_lower(Name) ++ ",{header, trailer" ++
xslapply(fun tmp_record_header/1, select("field", E)) ++ "}).\n";
tmp_record_header(E) ->
built_in_rules(fun tmp_record_header/1, E).