Skip to content

Instantly share code, notes, and snippets.

@tdx
Created March 6, 2014 17:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tdx/9394644 to your computer and use it in GitHub Desktop.
Save tdx/9394644 to your computer and use it in GitHub Desktop.
#!/usr/bin/env escript
-mode(compile).
%%
main([]) -> parse();
main(["help"]) -> usage();
main(["--help"]) -> usage();
main(["-h"]) -> usage();
main([Profile|Rest]) -> parse(Profile, Rest);
main(_) -> usage().
-record(tr_model, {
root = <<>>,
profiles = [],
cur_profile = "",
cur_object = "",
cur_parameter = "",
param_idx = 0
}).
-record(syntax, {
type = <<>> :: binary(),
sub_type = <<>> :: binary(), % for list type
default :: term(),
size = 0 :: non_neg_integer(),
range :: term(),
enum = [] :: list()
}).
-record(tr_parameter, {
id = 0 :: non_neg_integer(),
ref = {<<>>,<<>>} :: {Obj::binary(),Param::binary()},
profile = <<>> :: binary(),
access = <<>> :: binary(),
forcedInform = false :: boolean(),
activeNotify = <<>> :: binary(),
status = <<>> :: binary(),
syntax = undefined :: undefined | #syntax{}
}).
-record(stp, {
cur_object = <<"">> :: binary() , % object name
cur_param = undefined :: undefined | #tr_parameter{},
cur_syntax = undefined :: undefined | #syntax{}
}).
usage() ->
io:format(
<<"~n tr_model TrWithProfile TrWithOtherVsn1 ... TrWithOtherVsnN~n~n">>,
[]).
parse() ->
parse(
"apps/cpe/xslt/tr-098-1-0-0.xml", % с профилями InternetGatewayDevice:1.1
["apps/cpe/xslt/tr-069-1-0-0.xml"]). % InternetGatewayDevice:1.0
parse(FileWithProfiles, AdditionalFiles) ->
mk_store(),
{ok,TrWithProfiles} = file:read_file(FileWithProfiles),
Opts = [
{event_fun, fun event/3},
{event_state, {#tr_model{},""}}
],
% 1. Ищем профили и параметры
{ok, {Model,_}, _} = xmerl_sax_parser:stream(TrWithProfiles, Opts),
#tr_model{
root = Root,
profiles = Profiles} = Model,
io:format("model: ~n RootObject: ~p~n profiles: ~p~n", [Root, Profiles]),
% 2. По найденным параметрам профилей заполняем свойства параметров
Opts2 = [
{event_fun, fun ev/3},
{event_state, {#stp{},""}}
],
xmerl_sax_parser:stream(TrWithProfiles, Opts2),
% 2.2 файлы с предыдущими версиями
% TODO: проверять что для того же RootObject ?
[
begin
{ok,TrBin} = file:read_file(TrFile),
xmerl_sax_parser:stream(TrBin, Opts2)
end || TrFile <- AdditionalFiles
],
% 3. dump tr_model ets to file
FSort = fun(A,B) -> A < B end,
L = lists:sort(FSort, ets:tab2list(tr_model)),
FName = "tr_model.txt",
file:write_file(FName, io_lib:format("~p.~n", [L])),
io:format(<<" Parameters: ~p~n Output: ~s~n">>,
[Model#tr_model.param_idx, FName]),
ok.
% Профили, объекты и параметры
event({startElement,_Uri,"model",_QName, Attrs}, _Location, {St,Chars}) ->
{get_model_name(St, Attrs),Chars};
event({startElement,_Uri,"profile",_QName, Attrs}, _Location, {St,Chars}) ->
{get_profile_name(St, Attrs),Chars};
event({startElement,_Uri,"object",_QName, Attrs}, _Location, {St,Chars})
when St#tr_model.cur_profile /= "" ->
{get_object_name(St, Attrs),Chars};
event({startElement,_Uri,"parameter",_QName, Attrs}, _Location, {St,Chars})
when St#tr_model.cur_object /= "" ->
{get_parameter_name(St, Attrs),Chars};
event({endElement,_Uri,"parameter",_QName}, _Location, {St,Chars})
when St#tr_model.cur_parameter /= "" ->
{St#tr_model{cur_parameter=""},Chars};
event({endElement,_Uri,"object",_QName}, _Location, {St,Chars})
when St#tr_model.cur_object /= "" ->
{St#tr_model{cur_object=""},Chars};
event({endElement,_Uri,"profile",_QName}, _Location, {St,Chars}) ->
{add_profile_to_root(St),Chars};
event(_What, _Location, St) ->
% io:format(<<"~p~n">>, [What]),
St.
% Свойства параметров
ev({startElement,_Uri,"object",_QName, Attrs}, _Location, {St,Chars})
when St#stp.cur_object == <<"">> ->
{get_object_name2(St, Attrs),Chars};
ev({startElement,_Uri,"parameter",_QName, Attrs}, _Location, {St,Chars})
when St#stp.cur_param == undefined ->
{get_parameter_name2(St, Attrs),Chars};
ev({startElement,_Uri,"syntax",_QName,_Attrs}, _Location, {St,Chars})
when St#stp.cur_syntax == undefined ->
{St#stp{cur_syntax = #syntax{}}, Chars};
ev({startElement,_Uri,LName,_QName,Attrs}, _Location, {St,Chars})
when St#stp.cur_param /= undefined,
St#stp.cur_syntax /= undefined ->
{handle_syntax_data(St, tb(LName), Attrs), Chars};
ev({endElement,_Uri,"syntax",_QName}, _Location, {St,Chars})
when St#stp.cur_param /= undefined,
St#stp.cur_syntax /= undefined ->
TrP = St#stp.cur_param,
NewTrP = TrP#tr_parameter{syntax = St#stp.cur_syntax},
store_parameter(NewTrP),
NewSt = St#stp{cur_syntax = undefined,
cur_param = NewTrP},
{NewSt,Chars};
ev({endElement,_Uri,"parameter",_QName}, _Location, {St,Chars})
when St#stp.cur_param /= undefined ->
store_parameter(St#stp.cur_param),
NewSt = St#stp{cur_syntax = undefined,
cur_param = undefined},
{NewSt,Chars};
ev({endElement,_Uri,"object",_QName}, _Location, {St,Chars})
when St#stp.cur_object /= <<"">> ->
{St#stp{cur_object= <<"">>},Chars};
% ev(What, _Location, {St,Chars}) when St#stp.cur_syntax /= undefined ->
% io:format(<<"~p~n">>, [What]),
% {St,Chars};
ev(_What, _Location, St) ->
St.
%%% ---------------------------------------------------------------------------
mk_store() ->
Tab = tr_model,
case ets:info(Tab) of
undefined -> ets:new(Tab, [set,public,named_table,{keypos,3}]);
_ -> ets:delete_all_objects(Tab)
end.
store_parameter(#tr_parameter{}=P) ->
true = ets:insert(tr_model, P).
get_parameter(Key) ->
case ets:lookup(tr_model, Key) of
[Val] -> {ok,Val};
[] -> false
end.
%%% ---------------------------------------------------------------------------
get_model_name(St, Attrs) ->
case attr_find("name", Attrs) of
{ok,Value} -> St#tr_model{root = Value};
false -> St
end.
get_profile_name(#tr_model{}=St, Attrs) ->
case attr_find("name", Attrs) of
{ok,Value} ->
% Profile = #tr_profile{name=Value},
St#tr_model{cur_profile = Value};
false -> St
end.
get_object_name(St, Attrs) ->
case attr_find("ref", Attrs) of
{ok,Value} -> St#tr_model{cur_object=Value};
false -> St
end.
get_parameter_name(#tr_model{cur_profile=Profile, cur_object=Obj}=St, Attrs) ->
case attr_find("ref", Attrs) of
{ok,Param} ->
Id = St#tr_model.param_idx,
add_parameter_to_profile(Profile,Obj,Param,Id),
St#tr_model{cur_parameter=Param,param_idx=Id+1};
false -> St
end.
%%% ---------------------------------------------------------------------------
get_object_name2(St, Attrs) ->
case attr_find_name_or_base(Attrs) of
{ok,Value} -> St#stp{cur_object=tb(Value)};
false -> St
end.
get_parameter_name2(#stp{cur_object=Obj}=St, Attrs) ->
case attr_find_name_or_base(Attrs) of
{ok,Param} ->
Key = {Obj,tb(Param)},
TrParam = get_parameter_attributes(Key, Attrs),
St#stp{cur_param=TrParam};
false -> St
end.
get_parameter_attributes(Key, Attrs) ->
case get_parameter(Key) of
{ok,TrP} ->
NewTrP = get_parameter_attributes2(TrP, Attrs),
store_parameter(NewTrP),
NewTrP;
false -> undefined
end.
get_parameter_attributes2(TrP, Attrs) ->
Access = attr_find("access" , Attrs, "readOnly"),
ActiveNotify = attr_find("activeNotify", Attrs, ""),
ForcedInform = case attr_find("forcedInform", Attrs, "0") of
"true" -> true;
_ -> false
end,
Status = attr_find("status", Attrs, ""),
TrP#tr_parameter{
status = Status,
access = Access,
activeNotify = ActiveNotify,
forcedInform = ForcedInform}.
attr_find_name_or_base(Attrs) ->
case attr_find("name", Attrs) of
{ok,Value} -> {ok,Value};
false ->
case attr_find("base", Attrs) of
{ok,Value} -> {ok,Value};
false -> false
end
end.
%%% ---------------------------------------------------------------------------
%%% Syntax
%%% ---------------------------------------------------------------------------
handle_syntax_data(St, <<"boolean">>=Type, _Attrs) ->
set_syntax_type(Type, St);
handle_syntax_data(St, <<"string">>=Type, _Attrs) ->
set_syntax_type(Type, St);
handle_syntax_data(St, <<"unsignedInt">>=Type, _Attrs) ->
set_syntax_type(Type, St);
handle_syntax_data(St, <<"int">>=Type, _Attrs) ->
set_syntax_type(Type, St);
handle_syntax_data(St, <<"list">>=Type, _Attrs) ->
set_syntax_type(Type, St);
handle_syntax_data(St, <<"dateTime">>=Type, _Attrs) ->
set_syntax_type(Type, St);
handle_syntax_data(St, <<"dataType">>, Attrs) ->
set_syntax_datatype(Attrs, St);
handle_syntax_data(St, <<"size">>, Attrs) ->
set_syntax_size(Attrs, St);
handle_syntax_data(St, <<"range">>, Attrs) ->
set_syntax_range(Attrs, St);
handle_syntax_data(St, <<"enumeration">>, Attrs) ->
add_enumeration(Attrs, St);
handle_syntax_data(St, <<"pattern">>, Attrs) ->
add_enumeration(Attrs, St);
handle_syntax_data(St, <<"default">>, Attrs) ->
set_syntax_default(Attrs, St);
% unknown
handle_syntax_data(St, LName, _Attrs) when LName /= <<"description">>,
LName /= <<"syntax">> ->
io:format("syntax_data: ~p~n", [LName]),
St;
handle_syntax_data(St, _LName, _Attrs) ->
St.
set_syntax_type(Type, #stp{cur_syntax=#syntax{type=Old}=Syn}=St)
when Old /= <<>> ->
NewSyn = Syn#syntax{sub_type = Type},
St#stp{cur_syntax = NewSyn};
set_syntax_type(Type, #stp{cur_syntax=#syntax{}=Syn}=St) ->
NewSyn = Syn#syntax{type = Type},
St#stp{cur_syntax = NewSyn}.
set_syntax_datatype(Attrs, #stp{cur_syntax=#syntax{}=Syn}=St) ->
DataType = attr_find("ref", Attrs, <<"">>),
NewSyn = Syn#syntax{type = DataType},
St#stp{cur_syntax = NewSyn}.
set_syntax_range(Attrs, #stp{cur_syntax=#syntax{}=Syn}=St) ->
MinInclusive = attr_find("minInclusive", Attrs, 0),
MaxInclusive = attr_find("maxInclusive", Attrs, <<"unbounded">>),
NewSyn = Syn#syntax{range = {MinInclusive, MaxInclusive}},
St#stp{cur_syntax = NewSyn}.
add_enumeration(Attrs, #stp{cur_syntax=#syntax{enum=Old}=Syn}=St) ->
Value = attr_find("value", Attrs, ""),
NewSyn = Syn#syntax{enum = Old ++ [Value]},
St#stp{cur_syntax = NewSyn}.
set_syntax_default(Attrs, #stp{cur_syntax=#syntax{type=Type}=Syn}=St) ->
Default = attr_find("value", Attrs, <<"unbounded">>),
TypeDefault = case catch default_to_type(Type,Default) of
{'EXIT', _} ->
TrP = St#stp.cur_param,
io:format("error type: ~p: ~p, value: ~p~n",
[TrP#tr_parameter.ref,Type,Default]),
Default;
Val -> Val
end,
NewSyn = Syn#syntax{default = TypeDefault},
St#stp{cur_syntax = NewSyn}.
set_syntax_size(Attrs, #stp{cur_syntax=#syntax{}=Syn}=St) ->
Size = attr_find("maxLength", Attrs, "0"),
NewSyn = Syn#syntax{size = list_to_integer(Size)},
St#stp{cur_syntax = NewSyn}.
default_to_type(<<"boolean">> , "true") -> true;
default_to_type(<<"boolean">> , "false") -> false;
default_to_type(<<"unsignedInt">>, "") -> ""; % wrong file ?
default_to_type(<<"unsignedInt">>, V) -> list_to_integer(V);
default_to_type(<<"int">> , V) -> list_to_integer(V);
default_to_type(_, V) -> V.
%%% ---------------------------------------------------------------------------
attr_find(AttrName, Attrs) ->
case lists:keyfind(AttrName, 3, Attrs) of
{_Uri,_Prefix,AttrName,Val} -> {ok,Val};
false -> false
end.
attr_find(AttrName, Attrs, Default) ->
case lists:keyfind(AttrName, 3, Attrs) of
{_Uri,_Prefix,AttrName,Val} -> Val;
false -> Default
end.
add_parameter_to_profile(ProfileName, Obj, ParamName, Id) ->
Param = #tr_parameter{ id = Id,
ref = {tb(Obj),tb(ParamName)},
profile = tb(ProfileName)},
store_parameter(Param).
add_profile_to_root(#tr_model{profiles=Old, cur_profile=Profile}=St) ->
St#tr_model{profiles=Old++[Profile], cur_profile=""}.
tb(L) when is_list(L) -> list_to_binary(L).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment