1 -module(lwes_esf_validator
).
3 -behaviour(gen_server
).
11 %% gen_server callbacks
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 %%====================================================================
31 %%====================================================================
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
46 RealEvents
= lists:delete (MetaEvent
, Events
),
47 {event
, _
, MetaAttrs
} = MetaEvent
,
48 [{event
, Name
, Attrs
++ MetaAttrs
} || {event
, Name
, Attrs
} <- RealEvents
]
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
60 validate_event (EventName1
, EventSpec
, Attrs
);
61 _
-> {error
, {event_undefined
, EventName
}}
65 [Stats
] = ets:lookup (?SPEC_TAB
, ?STATS_KEY
),
68 %%====================================================================
69 %% gen_server callbacks
70 %%====================================================================
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}),
80 handle_call ({state
}, _From
, State
) ->
81 {reply
, State
, State
}.
83 handle_cast (_Msg
, State
) ->
86 handle_info (_Info
, State
) ->
89 terminate (_Reason
, _State
) ->
90 ets:delete (?SPEC_TAB
),
93 code_change (_OldVsn
, State
, _Extra
) ->
96 %%--------------------------------------------------------------------
97 %%% Internal functions
98 %%--------------------------------------------------------------------
100 add_event (ESFName
, Event
) ->
101 { event
, EventName
, Attributes
} = Event
,
102 Attributes1
= [ A
|| {attribute
, A
} <- Attributes
],
103 { RequiredAttrs
, OptionalAttrs
} =
105 fun ({_
, _
, _
, Qualifier
}) -> Qualifier
== required
end,
107 ets:insert (?SPEC_TAB
,
108 {{ ESFName
, EventName
}, { RequiredAttrs
, OptionalAttrs
}}).
110 validate_event (EventName
, { RequiredSpec
, OptionalSpec
}, EventAttrs
) ->
111 case validate_unique (EventName
, EventAttrs
, []) of
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}),
122 ErrorUnique
-> ErrorUnique
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
]);
131 {error
, {field_duplicate
, AttrName
, EventName
}}
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
140 {error
, {field_missing
, AttributeName_S
, EventName
}};
142 case validate_attribute (EventName
, H
, Attr
) of
144 EventAttrs1
= lists:delete (Attr
, EventAttrs
),
145 validate_required (EventName
, T
, EventAttrs1
);
150 validate_optional (_
, _OptionalSpec
, []) -> ok
;
152 validate_optional (EventName
, OptionalSpec
, [{_Type
, AttrName
, _
} = H
| T
]) ->
153 case lists:keyfind (AttrName
, 2, OptionalSpec
) of
155 {error
, {field_undefined
, AttrName
, EventName
}};
157 case validate_attribute (EventName
, Spec
, H
) of
158 ok
-> validate_optional (EventName
, OptionalSpec
, T
);
163 validate_attribute (EventName
, {Type_S
, AttrName_S
, _
, _
}, {Type
, AttrName
, _
}) ->
164 case AttrName_S
== AttrName andalso Type_S
== Type
of
166 {error
, {field_type_mismatch
, AttrName
, EventName
, Type
, Type_S
}};
170 parse (file
, FileName
) ->
171 { ok
, InFile
} = file:open(FileName
, [read
]),
172 Acc
= loop (InFile
, []),
174 {ok
, ParseTree
} = ?
PARSER:parse (Acc
),
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
]);
182 error_logger:error_msg ("failed to read ESF file"),
188 %%--------------------------------------------------------------------
190 %%--------------------------------------------------------------------
193 -include_lib("eunit/include/eunit.hrl").
196 parse_string (String
) ->
197 {ok
, Tokens
, _EndLine
} = ?
LEXER:string (String
),
198 {ok
, ParseTree
} = ?
PARSER:parse ( Tokens
),
202 EventDefs
= parse_string(
204 required int32 i = 1;
210 Validate
= fun (Attrs
) ->
212 ets:delete (?SPEC_TAB
)
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
),
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"}},
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">>}])
277 [{attribute
, {int32
, <<"i">>, undefined
, undefined
}}]}],
286 [{attribute
, {int32
, <<"i">>, undefined
, undefined
}},
287 {attribute
, {uint32
, <<"j">>, undefined
, undefined
}}]}],
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
308 [{attribute
, {int32
, <<"i">>, undefined
, undefined
}},
309 {attribute
, {uint32
, <<"j">>, undefined
, undefined
}}]}],
311 # comments should be ignored
322 [{attribute
, {int32
, <<"i">>, "1", undefined
}},
323 {attribute
, {uint32
, <<"j">>, undefined
, undefined
}}]}],
325 # comments should be ignored
329 int32 i = 1; # we can set defaults
336 [{attribute
, {int32
, <<"i">>, "-1", undefined
}},
337 {attribute
, {uint32
, <<"j">>, undefined
, undefined
}}]}],
341 int32 i = -1; # we can set negative defaults
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
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
372 [{attribute
, {boolean
, <<"i">>, "true", undefined
}},
373 {attribute
, {boolean
, <<"j">>, "false", undefined
}}]}],
377 boolean i = true; # we can set defaults for boolean
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\";
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
410 [{attribute
, {byte
, <<"i">>, "1",undefined
}}]}],
414 byte i = 1; # define 'byte' attribute
420 [{attribute
, {nullable_int32_array
, <<"i">>, undefined
, nullable
}}]}],
424 nullable int32 i[16]; # define array attribute
430 [{attribute
, {int32_array
, <<"i">>, undefined
, undefined
}}]}],
434 int32 i[16]; # define array attribute
440 [{attribute
, {int32_array
, <<"i">>, undefined
, required
}}]}],
444 required int32 i[16]; # define array attribute
449 [ ?
_assertEqual (E
, parse_string(TC
)) || {E
, TC
} <- TestCases
].