update ChangeLog
[lwes-erlang/github-mirror.git] / src / lwes_esf_validator.erl
blob98a4f2b8d2462114c614fd7b0fa32f2eeb87ae06
1 -module(lwes_esf_validator).
3 -behaviour(gen_server).
5 %% API
6 -export([start_link/0,
7 add_esf/2,
8 validate/2,
9 stats/0]).
11 %% gen_server callbacks
12 -export([init/1,
13 handle_call/3,
14 handle_cast/2,
15 handle_info/2,
16 terminate/2,
17 code_change/3]).
19 -include_lib ("lwes.hrl").
21 -define (SPEC_TAB, lwes_esf_specs).
23 -define(LEXER, lwes_esf_lexer).
24 -define(PARSER, lwes_esf_parser).
26 -define(META_EVENT, "MetaEventInfo").
27 -define(STATS_KEY, stats).
29 %%====================================================================
30 %% API
31 %%====================================================================
32 start_link () ->
33 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
35 add_esf (ESFName, ESFFile) ->
36 Events = parse (file, ESFFile),
37 add_esf_events(ESFName, Events).
39 add_esf_events(ESFName, Events) ->
40 % if 'MetaEventInfo' is defined we need to merge the attribute
41 %specification from the 'Meta Event' to the specification of
42 % all other 'real' events
43 Events1 = case lists:keyfind (?META_EVENT, 2, Events) of
44 false -> Events;
45 MetaEvent ->
46 RealEvents = lists:delete (MetaEvent, Events),
47 {event, _, MetaAttrs} = MetaEvent,
48 [{event, Name, Attrs ++ MetaAttrs } || {event, Name, Attrs} <- RealEvents]
49 end,
50 lists:foreach (fun (E) -> add_event (ESFName, E) end, Events1).
52 validate (ESFName, #lwes_event {name = EventName, attrs = Attrs} = _Event) ->
53 ets:update_counter (?SPEC_TAB, ?STATS_KEY, {2,1}),
55 EventName1 = lwes_util:any_to_list (EventName),
56 Key = { ESFName, EventName1 },
58 case ets:lookup (?SPEC_TAB, Key) of
59 [{_, EventSpec}] ->
60 validate_event (EventName1, EventSpec, Attrs);
61 _ -> {error, {event_undefined, EventName}}
62 end.
64 stats () ->
65 [Stats] = ets:lookup (?SPEC_TAB, ?STATS_KEY),
66 Stats.
68 %%====================================================================
69 %% gen_server callbacks
70 %%====================================================================
72 init ([]) ->
73 % make sure terminate is called
74 process_flag (trap_exit, true),
76 ets:new (?SPEC_TAB, [set, public, named_table, {keypos, 1}]),
77 ets:insert (?SPEC_TAB,{?STATS_KEY, 0, 0}),
78 { ok, {} }.
80 handle_call ({state}, _From, State) ->
81 {reply, State, State }.
83 handle_cast (_Msg, State) ->
84 { noreply, State }.
86 handle_info (_Info, State) ->
87 { noreply, State }.
89 terminate (_Reason, _State) ->
90 ets:delete (?SPEC_TAB),
91 ok.
93 code_change (_OldVsn, State, _Extra) ->
94 { ok, State }.
96 %%--------------------------------------------------------------------
97 %%% Internal functions
98 %%--------------------------------------------------------------------
100 add_event (ESFName, Event) ->
101 { event, EventName, Attributes } = Event,
102 Attributes1 = [ A || {attribute, A} <- Attributes ],
103 { RequiredAttrs, OptionalAttrs } =
104 lists:partition(
105 fun ({_, _, _, Qualifier}) -> Qualifier == required end,
106 Attributes1),
107 ets:insert (?SPEC_TAB,
108 {{ ESFName, EventName }, { RequiredAttrs, OptionalAttrs }}).
110 validate_event (EventName, { RequiredSpec, OptionalSpec }, EventAttrs) ->
111 case validate_unique (EventName, EventAttrs, []) of
112 ok ->
113 case validate_required (EventName, RequiredSpec, EventAttrs) of
114 {ok, OptionalAttrs} ->
115 case validate_optional (EventName, OptionalSpec, OptionalAttrs) of
116 ok -> ets:update_counter (?SPEC_TAB, ?STATS_KEY, {3,1}),
118 ErrorOpt -> ErrorOpt
119 end;
120 ErrorReq -> ErrorReq
121 end;
122 ErrorUnique -> ErrorUnique
123 end.
126 validate_unique (_, [], _) -> ok;
127 validate_unique (EventName, [{_, AttrName, _} | T], AttrsFound) ->
128 case lists:keyfind (AttrName, 1, AttrsFound) of
129 false -> validate_unique(EventName, T, [{AttrName} | AttrsFound]);
130 _ ->
131 {error, {field_duplicate, AttrName, EventName}}
132 end.
134 validate_required (_, [], EventAttrs) -> {ok, EventAttrs};
136 validate_required (EventName, [{_Type_S,
137 AttributeName_S, _, _} = H| T], EventAttrs) ->
138 case lists:keyfind (AttributeName_S, 2, EventAttrs) of
139 false ->
140 {error, {field_missing, AttributeName_S, EventName}};
141 Attr ->
142 case validate_attribute (EventName, H, Attr) of
143 ok ->
144 EventAttrs1 = lists:delete (Attr, EventAttrs),
145 validate_required (EventName, T, EventAttrs1);
146 Error -> Error
148 end.
150 validate_optional (_, _OptionalSpec, []) -> ok;
152 validate_optional (EventName, OptionalSpec, [{_Type, AttrName, _} = H | T]) ->
153 case lists:keyfind (AttrName, 2, OptionalSpec) of
154 false ->
155 {error, {field_undefined, AttrName, EventName}};
156 Spec ->
157 case validate_attribute (EventName, Spec, H) of
158 ok -> validate_optional (EventName, OptionalSpec, T);
159 Error -> Error
161 end.
163 validate_attribute (EventName, {Type_S, AttrName_S, _, _}, {Type, AttrName, _}) ->
164 case AttrName_S == AttrName andalso Type_S == Type of
165 false ->
166 {error, {field_type_mismatch, AttrName, EventName, Type, Type_S}};
167 _ -> ok
168 end.
170 parse (file, FileName) ->
171 { ok, InFile } = file:open(FileName, [read]),
172 Acc = loop (InFile, []),
173 file:close (InFile),
174 {ok, ParseTree} = ?PARSER:parse (Acc),
175 ParseTree.
177 loop (InFile, Acc) ->
178 case io:request(InFile, { get_until, prompt, ?LEXER, token, [1] } ) of
179 { ok, Toks, _EndLine } ->
180 loop (InFile,Acc ++ [Toks]);
181 { error, token } ->
182 error_logger:error_msg ("failed to read ESF file"),
183 Acc;
184 { eof, _ } ->
186 end.
188 %%--------------------------------------------------------------------
189 %%% Test functions
190 %%--------------------------------------------------------------------
192 -ifdef(TEST).
193 -include_lib("eunit/include/eunit.hrl").
196 parse_string (String) ->
197 {ok, Tokens, _EndLine} = ?LEXER:string (String),
198 {ok, ParseTree} = ?PARSER:parse ( Tokens ),
199 ParseTree.
201 validate_test_() ->
202 EventDefs = parse_string(
203 "TestEvent {
204 required int32 i = 1;
205 required boolean j;
206 int32 k = 1;
207 string l;
210 Validate = fun (Attrs) ->
212 ets:delete (?SPEC_TAB)
213 catch _:_ -> 0 end,
214 ets:new (?SPEC_TAB, [set, public, named_table, {keypos, 1}]),
215 ets:insert (?SPEC_TAB,{?STATS_KEY, 0, 0}),
216 add_esf_events(test_esf, EventDefs),
217 Valid = validate(test_esf, #lwes_event {name = <<"TestEvent">>, attrs = Attrs}),
218 ets:delete (?SPEC_TAB),
219 Valid
220 end,
222 ?_assertEqual(ok, % all fields
223 Validate([{int32,<<"i">>,314159},
224 {boolean,<<"j">>,false},
225 {int32,<<"k">>,654321},
226 {string,<<"l">>,<<"foo">>}])
228 % NOTE: Default values aren't patched in anywhere currently.
229 % So, let this fail validation for now.
230 %?assertEqual(true, % missing required field with default
231 % Validate([ {boolean,<<"j">>,false},
232 % {int32,<<"k">>,654321},
233 % {string,<<"l">>,<<"foo">>}])
235 ?_assertEqual({error, {field_missing, <<"j">>, "TestEvent"}},
236 % missing required field (no default)
237 Validate([{int32,<<"i">>,314159},
238 {int32,<<"k">>,654321},
239 {string,<<"l">>,<<"foo">>}])
241 ?_assertEqual({error, {field_duplicate, <<"j">>, "TestEvent"}},
242 % duplicate field
243 Validate([{int32,<<"i">>,314159},
244 {boolean,<<"j">>,false},
245 {boolean,<<"j">>,false},
246 {int32,<<"k">>,654321},
247 {string,<<"l">>,<<"foo">>}])
249 ?_assertEqual({error, {field_type_mismatch, <<"i">>, "TestEvent", boolean, int32}},
250 % type error, required field
251 Validate([{boolean,<<"i">>,true},
252 {boolean,<<"j">>,false},
253 {int32,<<"k">>,654321},
254 {string,<<"l">>,<<"foo">>}])
256 ?_assertEqual({error, {field_type_mismatch, <<"k">>, "TestEvent", string, int32}},
257 % type error, optional field
258 Validate([{int32,<<"i">>,314159},
259 {boolean,<<"j">>,false},
260 {string,<<"k">>,<<"blah">>},
261 {string,<<"l">>,<<"foo">>}])
263 ?_assertEqual({error, {field_undefined, <<"extra">>, "TestEvent"}},
264 % extra undefined field
265 Validate([{int32,<<"i">>,314159},
266 {boolean,<<"j">>,false},
267 {int32,<<"k">>,654321},
268 {int32,<<"extra">>,12345},
269 {string,<<"l">>,<<"foo">>}])
273 parse_test_() ->
274 TestCases = [
276 [{event,"A",
277 [{attribute, {int32, <<"i">>, undefined, undefined}}]}],
280 int32 i;
285 [{event,"A",
286 [{attribute, {int32, <<"i">>, undefined, undefined}},
287 {attribute, {uint32, <<"j">>, undefined, undefined}}]}],
290 int32 i;
291 uint32 j;
296 [{event,"A",
297 [{attribute, {int32, <<"i">>, undefined, undefined}},
298 {attribute, {uint32, <<"j">>, undefined, undefined}}]}],
301 int32 i; # comments should be ignored
302 uint32 j; # comments should be ignored
307 [{event,"A",
308 [{attribute, {int32, <<"i">>, undefined, undefined}},
309 {attribute, {uint32, <<"j">>, undefined, undefined}}]}],
311 # comments should be ignored
312 # like this one
315 int32 i;
316 uint32 j;
321 [{event,"A",
322 [{attribute, {int32, <<"i">>, "1", undefined}},
323 {attribute, {uint32, <<"j">>, undefined, undefined}}]}],
325 # comments should be ignored
326 # like this one
329 int32 i = 1; # we can set defaults
330 uint32 j;
335 [{event,"A",
336 [{attribute, {int32, <<"i">>, "-1", undefined}},
337 {attribute, {uint32, <<"j">>, undefined, undefined}}]}],
341 int32 i = -1; # we can set negative defaults
342 uint32 j;
347 [{event,"A",
348 [{attribute, {float, <<"i">>, "-0.1234", undefined}},
349 {attribute, {float, <<"j">>, "12.8999", undefined}}]}],
353 float i = -0.1234; # we can set negative defaults for float
354 float j = 12.8999;
359 [{event,"A",
360 [{attribute, {double, <<"i">>, "-0.1234", undefined}},
361 {attribute, {double, <<"j">>, "12.8999", undefined}}]}],
365 double i = -0.1234; # we can set negative defaults for double
366 double j = 12.8999;
371 [{event,"A",
372 [{attribute, {boolean, <<"i">>, "true", undefined}},
373 {attribute, {boolean, <<"j">>, "false", undefined}}]}],
377 boolean i = true; # we can set defaults for boolean
378 boolean j = false;
383 [{event,"A",
384 [{attribute, {string, <<"i">>, "\"hello\"", undefined}},
385 {attribute, {string, <<"j">>, "\"hello\nworld\"", undefined}}]}],
389 string i = \"hello\"; # we can set defaults for string
390 string j = \"hello\nworld\";
395 [{event,"A",
396 [{attribute, {uint32, <<"i">>, "1", optional}},
397 {attribute, {int32, <<"j">>, "-1", nullable}},
398 {attribute, {uint32, <<"k">>, "2", required}}]}],
402 optional uint32 i = 1; # make this optional
403 nullable int32 j = -1; # nullable
404 required uint32 k = 2; # required
409 [{event,"A",
410 [{attribute, {byte, <<"i">>, "1",undefined}}]}],
414 byte i = 1; # define 'byte' attribute
419 [{event,"A",
420 [{attribute, {nullable_int32_array, <<"i">>, undefined, nullable}}]}],
424 nullable int32 i[16]; # define array attribute
429 [{event,"A",
430 [{attribute, {int32_array, <<"i">>, undefined, undefined}}]}],
434 int32 i[16]; # define array attribute
439 [{event,"A",
440 [{attribute, {int32_array, <<"i">>, undefined, required}}]}],
444 required int32 i[16]; # define array attribute
449 [ ?_assertEqual (E, parse_string(TC)) || {E, TC} <- TestCases ].
451 -endif.