%% vim: tabstop=8:shiftwidth=4
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2014-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(asn1ct_gen_check).
-export([emit/4]).
-import(asn1ct_gen, [emit/1]).
-include("asn1_records.hrl").
emit(Gen, Type, Default, Value) ->
Key = {Type,Default},
DoGen = fun(Fd, Name) ->
file:write(Fd, gen(Gen, Name, Type, Default))
end,
emit(" case "),
asn1ct_func:call_gen("is_default_", Key, DoGen, [Value]),
emit([" of",nl,
"true -> {[],0};",nl,
"false ->",nl]).
gen(#gen{pack=Pack}=Gen, Name, #type{def=T}, Default) ->
DefMarker = case Pack of
record -> "asn1_DEFAULT";
map -> atom_to_list(?MISSING_IN_MAP)
end,
NameStr = atom_to_list(Name),
[NameStr,"(",DefMarker,") ->\n",
"true;\n"|
case do_gen(Gen, T, Default) of
{literal,Literal} ->
[NameStr,"(Def) when Def =:= ",term2str(Literal)," ->\n",
"true;\n",
NameStr,"(_) ->\n","false.\n\n"];
{exception,Func,Args} ->
[NameStr,"(Value) ->\n",
"try ",Func,"(Value",arg2str(Args),") of\n",
"_ -> true\n"
"catch throw:false -> false\n"
"end.\n\n"]
end].
do_gen(_Gen, _, asn1_NOVALUE) ->
{literal,asn1_NOVALUE};
do_gen(Gen, #'Externaltypereference'{module=M,type=T}, Default) ->
#typedef{typespec=#type{def=Td}} = asn1_db:dbget(M, T),
do_gen(Gen, Td, Default);
do_gen(_Gen, 'BOOLEAN', Default) ->
{literal,Default};
do_gen(_Gen, {'BIT STRING',[]}, Default) ->
true = is_bitstring(Default), %Assertion.
case asn1ct:use_legacy_types() of
false ->
{literal,Default};
true ->
{exception,need(check_legacy_bitstring, 2),[Default]}
end;
do_gen(_Gen, {'BIT STRING',[_|_]=NBL}, Default) ->
do_named_bitstring(NBL, Default);
do_gen(_Gen, {'ENUMERATED',_}, Default) ->
{literal,Default};
do_gen(_Gen, 'INTEGER', Default) ->
{literal,Default};
do_gen(_Gen, {'INTEGER',NNL}, Default) ->
{exception,need(check_int, 3),[Default,NNL]};
do_gen(_Gen, 'NULL', Default) ->
{literal,Default};
do_gen(_Gen, 'OCTET STRING', Default) ->
true = is_binary(Default), %Assertion.
case asn1ct:use_legacy_types() of
false ->
{literal,Default};
true ->
{exception,need(check_octetstring, 2),[Default]}
end;
do_gen(_Gen, 'OBJECT IDENTIFIER', Default0) ->
Default = pre_process_oid(Default0),
{exception,need(check_objectidentifier, 2),[Default]};
do_gen(Gen, {'CHOICE',Cs}, Default) ->
{Tag,Value} = Default,
[Type] = [Type || #'ComponentType'{name=T,typespec=Type} <- Cs,
T =:= Tag],
case do_gen(Gen, Type#type.def, Value) of
{literal,Lit} ->
{literal,{Tag,Lit}};
{exception,Func0,Args} ->
Key = {Tag,Func0,Args},
DoGen = fun(Fd, Name) ->
S = gen_choice(Name, Tag, Func0, Args),
ok = file:write(Fd, S)
end,
Func = asn1ct_func:call_gen("is_default_choice", Key, DoGen),
{exception,atom_to_list(Func),[]}
end;
do_gen(Gen, #'SEQUENCE'{components=Cs}, Default) ->
do_seq_set(Gen, Cs, Default);
do_gen(Gen, {'SEQUENCE OF',Type}, Default) ->
do_sof(Gen, Type, Default);
do_gen(Gen, #'SET'{components=Cs}, Default) ->
do_seq_set(Gen, Cs, Default);
do_gen(Gen, {'SET OF',Type}, Default) ->
do_sof(Gen, Type, Default);
do_gen(_Gen, Type, Default) ->
case asn1ct_gen:unify_if_string(Type) of
restrictedstring ->
{exception,need(check_restrictedstring, 2),[Default]};
_ ->
%% Open type. Do our best.
{literal,Default}
end.
do_named_bitstring(NBL, Default0) when is_list(Default0) ->
Default = lists:sort(Default0),
Bs = asn1ct_gen:named_bitstring_value(Default, NBL),
Func = case asn1ct:use_legacy_types() of
false -> check_named_bitstring;
true -> check_legacy_named_bitstring
end,
{exception,need(Func, 4),[Default,Bs,bit_size(Bs)]};
do_named_bitstring(_, Default) when is_bitstring(Default) ->
Func = case asn1ct:use_legacy_types() of
false -> check_named_bitstring;
true -> check_legacy_named_bitstring
end,
{exception,need(Func, 3),[Default,bit_size(Default)]}.
do_seq_set(#gen{pack=record}=Gen, Cs0, Default) ->
Tag = element(1, Default),
Cs1 = [T || #'ComponentType'{typespec=T} <- Cs0],
Cs = components(Gen, Cs1, tl(tuple_to_list(Default))),
case are_all_literals(Cs) of
true ->
Literal = list_to_tuple([Tag|[L || {literal,L} <- Cs]]),
{literal,Literal};
false ->
Key = {Cs,Default},
DoGen = fun(Fd, Name) ->
S = gen_components(Name, Tag, Cs),
ok = file:write(Fd, S)
end,
Func = asn1ct_func:call_gen("is_default_cs_", Key, DoGen),
{exception,atom_to_list(Func),[]}
end;
do_seq_set(#gen{pack=map}=Gen, Cs0, Default) ->
Cs1 = [{N,T} || #'ComponentType'{name=N,typespec=T} <- Cs0],
Cs = map_components(Gen, Cs1, Default),
AllLiterals = lists:all(fun({_,{literal,_}}) -> true;
({_,_}) -> false
end, Cs),
case AllLiterals of
true ->
L = [{Name,Lit} || {Name,{literal,Lit}} <- Cs],
{literal,maps:from_list(L)};
false ->
Key = {Cs,Default},
DoGen = fun(Fd, Name) ->
S = gen_map_components(Name, Cs),
ok = file:write(Fd, S)
end,
Func = asn1ct_func:call_gen("is_default_cs_", Key, DoGen),
{exception,atom_to_list(Func),[]}
end.
do_sof(Gen, Type, Default0) ->
Default = lists:sort(Default0),
Cs0 = lists:duplicate(length(Default), Type),
Cs = components(Gen, Cs0, Default),
case are_all_literals(Cs) of
true ->
Literal = [Lit || {literal,Lit} <- Cs],
{exception,need(check_literal_sof, 2),[Literal]};
false ->
Key = Cs,
DoGen = fun(Fd, Name) ->
S = gen_sof(Name, Cs),
ok = file:write(Fd, S)
end,
Func = asn1ct_func:call_gen("is_default_sof", Key, DoGen),
{exception,atom_to_list(Func),[]}
end.
are_all_literals([{literal,_}|T]) ->
are_all_literals(T);
are_all_literals([_|_]) ->
false;
are_all_literals([]) -> true.
gen_components(Name, Tag, Cs) ->
[atom_to_list(Name),"(Value) ->\n",
"case Value of\n",
"{",term2str(Tag)|gen_cs_1(Cs, 1, [])].
gen_cs_1([{literal,Lit}|T], I, Acc) ->
[",\n",term2str(Lit)|gen_cs_1(T, I, Acc)];
gen_cs_1([H|T], I, Acc) ->
Var = "E"++integer_to_list(I),
[",\n",Var|gen_cs_1(T, I+1, [{Var,H}|Acc])];
gen_cs_1([], _, Acc) ->
["} ->\n"|gen_cs_2(Acc, "")].
gen_cs_2([{Var,{exception,Func,Args}}|T], Sep) ->
[Sep,Func,"(",Var,arg2str(Args),")"|gen_cs_2(T, ",\n")];
gen_cs_2([], _) ->
[";\n",
"_ ->\n"
"throw(false)\n"
"end.\n"].
gen_map_components(Name, Cs) ->
[atom_to_list(Name),"(Value) ->\n",
"case Value of\n",
"#{"|gen_map_cs_1(Cs, 1, "", [])].
gen_map_cs_1([{Name,{literal,Lit}}|T], I, Sep, Acc) ->
Var = "E"++integer_to_list(I),
G = Var ++ " =:= " ++ term2str(Lit),
[Sep,term2str(Name),":=",Var|
gen_map_cs_1(T, I+1, ",\n", [{guard,G}|Acc])];
gen_map_cs_1([{Name,Exc}|T], I, Sep, Acc) ->
Var = "E"++integer_to_list(I),
[Sep,term2str(Name),":=",Var|
gen_map_cs_1(T, I+1, ",\n", [{exc,{Var,Exc}}|Acc])];
gen_map_cs_1([], _, _, Acc) ->
G = lists:join(", ", [S || {guard,S} <- Acc]),
Exc = [E || {exc,E} <- Acc],
Body = gen_map_cs_2(Exc, ""),
case G of
[] ->
["} ->\n"|Body];
[_|_] ->
["} when ",G," ->\n"|Body]
end.
gen_map_cs_2([{Var,{exception,Func,Args}}|T], Sep) ->
[Sep,Func,"(",Var,arg2str(Args),")"|gen_map_cs_2(T, ",\n")];
gen_map_cs_2([], _) ->
[";\n",
"_ ->\n"
"throw(false)\n"
"end.\n"].
gen_sof(Name, Cs) ->
[atom_to_list(Name),"(Value) ->\n",
"case length(Value) of\n",
integer_to_list(length(Cs))," -> ok;\n"
"_ -> throw(false)\n"
"end,\n"
"T0 = lists:sort(Value)"|gen_sof_1(Cs, 1)].
gen_sof_1([{exception,Func,Args}|Cs], I) ->
NumStr = integer_to_list(I),
H = "H" ++ NumStr,
T = "T" ++ NumStr,
Prev = "T" ++ integer_to_list(I-1),
[",\n",
"[",H,case Cs of
[] -> [];
[_|_] -> ["|",T]
end,"] = ",Prev,",\n",
Func,"(",H,arg2str(Args),")"|gen_sof_1(Cs, I+1)];
gen_sof_1([], _) ->
".\n".
components(Gen, [#type{def=Def}|Ts], [V|Vs]) ->
[do_gen(Gen, Def, V)|components(Gen, Ts, Vs)];
components(_Gen, [], []) -> [].
map_components(Gen, [{Name,#type{def=Def}}|Ts], Value) ->
case maps:find(Name, Value) of
{ok,V} ->
[{Name,do_gen(Gen, Def, V)}|map_components(Gen, Ts, Value)];
error ->
map_components(Gen, Ts, Value)
end;
map_components(_Gen, [], _Value) -> [].
gen_choice(Name, Tag, Func, Args) ->
NameStr = atom_to_list(Name),
[NameStr,"({",term2str(Tag),",Value}) ->\n"
" ",Func,"(Value",arg2str(Args),");\n",
NameStr,"(_) ->\n"
" throw(false).\n"].
pre_process_oid(Oid) ->
Reserved = reserved_oid(),
pre_process_oid(tuple_to_list(Oid), Reserved, []).
pre_process_oid([H|T]=Tail, Res0, Acc) ->
case lists:keyfind(H, 2, Res0) of
false ->
{lists:reverse(Acc),Tail};
{Names0,H,Res} ->
Names = case is_list(Names0) of
false -> [Names0];
true -> Names0
end,
Keys = [H|Names],
pre_process_oid(T, Res, [Keys|Acc])
end.
reserved_oid() ->
[{['itu-t',ccitt],0,
[{recommendation,0,[]},
{question,1,[]},
{administration,2,[]},
{'network-operator',3,[]},
{'identified-organization',4,[]}]},
{iso,1,[{standard,0,[]},
{'member-body',2,[]},
{'identified-organization',3,[]}]},
{['joint-iso-itu-t','joint-iso-ccitt'],2,[]}].
arg2str(Args) ->
[", "++term2str(Arg) || Arg <- Args].
term2str(T) ->
io_lib:format("~w", [T]).
need(F, A) ->
asn1ct_func:need({check,F,A}),
atom_to_list(F).