diff options
Diffstat (limited to 'lib/wx/api_gen/wx_gen.erl')
-rw-r--r-- | lib/wx/api_gen/wx_gen.erl | 1443 |
1 files changed, 1443 insertions, 0 deletions
diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl new file mode 100644 index 0000000000..50dd2d6f51 --- /dev/null +++ b/lib/wx/api_gen/wx_gen.erl @@ -0,0 +1,1443 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% Api wrapper generator + +-module(wx_gen). +-export([code/0,xml/0]). + +-include("wx_gen.hrl"). + +-include_lib("xmerl/include/xmerl.hrl"). + +-import(lists, [foldl/3,foldr/3,reverse/1, keysearch/3, map/2, filter/2]). +-import(proplists, [get_value/2,get_value/3]). + +-compile(export_all). + +code() -> safe(fun gen_code/0,true). +xml() -> safe(fun gen_xml/0,true). + +devcode() -> erase(),safe(fun gen_code/0,false). + +safe(What, QuitOnErr) -> + try + What(), + io:format("Completed succesfully~n~n", []), + QuitOnErr andalso gen_util:halt(0) + catch Err:Reason -> + io:format("Error in ~p ~p~n", [get(current_class),get(current_func)]), + erlang:display({Err,Reason, erlang:get_stacktrace()}), + catch gen_util:close(), + QuitOnErr andalso gen_util:halt(1) + end. + +gen_code() -> + {ok, Defs0} = file:consult("wxapi.conf"), + %% {ok, Defs0} = file:consult("test.conf"), + erase(func_id), + put(class_id, 10), %% Start from 10 using the other as special + Defs1 = init_defs(Defs0), + Defs2 = parse_defs(Defs1, []), + parse_enums([File || {{include, File},_} <- get()]), + Defs = translate_enums(Defs2), + wx_gen_erl:gen(Defs), + wx_gen_cpp:gen(Defs), + ok. + +gen_xml() -> +%% {ok, Defs} = file:consult("wxapi.conf"), + +%% Rel = reverse(tl(reverse(os:cmd("wx-config --release")))), +%% Dir = " /usr/include/wx-" ++ Rel ++ "/wx/", +%% Files0 = [Dir ++ File || {class, File, _, _, _} <- Defs], +%% Files1 = [Dir ++ File || {doxygen, File} <- Defs], +%% ok = file:write_file("wxapi.files", list_to_binary("INPUT = "++Files0++Files1)), + ok. + +-record(hs,{alias,skip,fs,fopt,ev,acc,info}). + +init_defs(List0) -> + List1 = to_lists(List0), + lists:map(fun mangle_info/1, List1). + +mangle_info(E={enum,Type0,SkipStr}) -> + Type = case is_atom(Type0) of true -> atom_to_list(Type0); false -> Type0 end, + put({enum, Type}, #enum{skip=SkipStr,as_atom=false}), %% as_atom=true}), + E; +mangle_info(E={const_skip,List}) -> + put(const_skip, [atom_to_list(M) || M <- List]), + E; +mangle_info(E={not_const,List}) -> + put(not_const, [atom_to_list(M) || M <- List]), + E; +mangle_info(E={gvars,List}) -> + A2L = fun({N,{T,C}}) -> {atom_to_list(N), {T,atom_to_list(C)}}; + ({N,C}) -> {atom_to_list(N), atom_to_list(C)} + end, + put(gvars, map(A2L,List)), + E; +mangle_info({class,CN,P,O,FL}) -> + Alias = get_value(alias,O, []), + Skip = get_value(skip, O, []), + Event = get_value(event,O, false), + Acc = get_value(acc, O, []), + {Fs,Fopts} = foldr(fun(FWO={F,FO},{Fl,Fopt}) when is_list(FO) -> + {[F|Fl],[FWO|Fopt]}; + (F,{Fl,Fopt}) -> + {[F|Fl], Fopt} + end, {[],[]}, FL), + {class,CN,P,#hs{alias=Alias,skip=Skip,fs=Fs,ev=Event,acc=Acc,info=O, + fopt=gb_trees:from_orddict(lists:sort(Fopts))}}. + +to_lists(Defs) -> + map(fun({class,C,P,O,Fs}) -> + {class,atom_to_list(C),atom_to_list(P),to_lists2(O),to_lists2(Fs)}; + (Skip) -> Skip + end, Defs). + +to_lists2(List) -> + map(fun(Skip = {const_skip, _List}) -> Skip; + (Skip = {not_const, _List}) -> Skip; + (Skip = {skip, _List}) -> Skip; + (Skip = {event, _List}) -> Skip; + (Skip = {acc, _List}) -> Skip; + (Skip = {doc, _List}) -> Skip; + (Skip = taylormade) -> Skip; + (Skip = {ifdef,_}) -> Skip; + (Skip = {erl_func, _Name}) -> Skip; + ({alias, AList}) -> {alias, [{atom_to_list(A),atom_to_list(B)} || {A,B} <- AList]}; + (Else) when is_atom(Else) -> atom_to_list(Else); + ({Else,W}) when is_atom(Else) -> {atom_to_list(Else),W}; + ({{Else,W},O}) when is_atom(Else) -> {{atom_to_list(Else),W},O}; + (Else) -> Else + end, List). + +parse_defs([{class,Name,Parent,Info}|Rest], Acc0) -> + {FileName, Type} = + case Parent of + "static" -> {Name ++ "_8h", static}; + _ -> {"class" ++ Name, class} + end, + Tab = ets:new(defs, [bag]), + Defs0 = load_members(FileName, Name, gb_trees:empty(), Tab, Type, Info), + + put(current_class, Name), + Class0 = #class{name=name(Name,Info),parent=Parent, + doc=get_value(doc,Info#hs.info,undefined), + file=FileName,options=Info#hs.info, id=next_id(class_id)}, + ParseClass = fun(Member,{Class,Dfs}) -> + parse_class(Member,Tab,Dfs,Class,Info) + end, + {Class1,Defs} = foldl(ParseClass,{Class0,Defs0},Info#hs.fs), + + Class2 = case Info#hs.ev of + false -> Class1; + Ev -> parse_attr(gb_trees:to_list(Defs), Class1, Ev, Info) + end, + Class = meta_info(Class2), + erase(current_class), + [erase(Del) || {Del = {loaded, _},_} <- get()], + %% ets:delete(Tab), keep it for debugging + parse_defs(Rest, [Class|Acc0]); +parse_defs([_|Rest], Acc) -> + parse_defs(Rest, Acc); +parse_defs([], Acc) -> reverse(Acc). + +meta_info(C=#class{name=CName,methods=Ms0}) -> + Ms = lists:append(Ms0), + HaveConstructor = + lists:keysearch(constructor, #method.method_type, Ms) =/= false, + case lists:keysearch(destructor, #method.method_type, Ms) of + false when HaveConstructor -> + Dest = #method{name="destroy",id=next_id(func_id), + method_type=destructor, params=[this(CName)]}, + C#class{methods = [[Dest]|Ms0]}; + false -> + C#class{abstract = true}; + _ -> + C + end. + +parse_class(Member0,Tab,Defs0,Class = #class{name=CName},Opts) -> + {Member,NoArgs} = case Member0 of + {_, _} -> Member0; + _ -> {Member0,all} + end, + case ets:lookup(Tab, Member) of + [] -> + case Member of + [$~|_] -> ignore; + _ -> + ?warning("Skipped Member ~p in ~p (not found in ~p)~n", + [Member,CName,Tab]) + end, + {Class,Defs0}; + Ms -> + case select_member(Ms, Class, Defs0, Opts) of + {[],Defs} -> + ?warning("Skipped Member ~p in ~p (not found in base)~n", + [Member,CName]), + {Class,Defs}; + {Selected,Defs} -> + Parsed = parse_members(Member,Selected,Defs,CName,Opts), + {add_method(Parsed,NoArgs,Class,Opts), Defs} + end + end. + +parse_members(MemberName, Members, Defs, Class, Opts) -> + ParseAll = + fun(Member,Acc) -> + try + case gb_trees:lookup(Member, Defs) of + {value,#xmlElement{name=memberdef,attributes=Attrs, + content=Data}} -> + MType = case keysearch(static,#xmlAttribute.name,Attrs) of + {value, #xmlAttribute{value = "yes"}} -> + static; + _ -> + member + end, + Virtual = + case keysearch(virt,#xmlAttribute.name,Attrs) of + {value, #xmlAttribute{value = "virtual"}} -> + true; + {value, #xmlAttribute{value = "non-virtual"}} -> + false; + _ -> + undefined + end, + [parse_member(Data,MType,Virtual,Opts)|Acc]; + none -> + Acc; + _Hmm -> + Acc + end + catch throw:skip_member -> + Acc + end + end, + case foldl(ParseAll,[],Members) of + [] -> + ?warning("Skipped ~p No public def found in ~p ~n", + [MemberName,Class]), + io:format("~p ~p~n",[MemberName, Members]), + []; + Res -> + Res + end. + + +parse_attr(Defs, Class, Ev, Info = #hs{acc=AccList0}) -> +% io:format("Parsing Class ~p~n", [Class#class.name]), + {Attrs, AccList} = parse_attr1(Defs, AccList0, Info, []), + case AccList of + [] -> + Class#class{attributes=Attrs, event=Ev}; + _ -> + Inherited = [{inherited, Inherit} || Inherit <- AccList], + Class#class{attributes=Attrs++Inherited, event=Ev} + end. + +parse_attr1([{{attr,_}, #xmlElement{content=C, attributes=Attrs}}|R], AttrList0, Opts, Res) -> + Parse = fun(Con, Ac) -> parse_param(Con, Opts, Ac) end, + Param0 = foldl(Parse, #param{}, drop_empty(C)), + case keysearch(prot, #xmlAttribute.name, Attrs) of + {value, #xmlAttribute{value = "public"}} -> + {Acc,AttrList} = attr_acc(Param0, AttrList0), + parse_attr1(R,AttrList,Opts, + [Param0#param{in=false,prot=public,acc=Acc}|Res]); + {value, #xmlAttribute{value = "protected"}} -> + {Acc,AttrList} = attr_acc(Param0, AttrList0), + parse_attr1(R,AttrList,Opts, + [Param0#param{in=false,prot=protected,acc=Acc}|Res]); + {value, #xmlAttribute{value = "private"}} -> + {Acc,AttrList} = attr_acc(Param0, AttrList0), + parse_attr1(R,AttrList,Opts, + [Param0#param{in=false,prot=private,acc=Acc}|Res]) + end; +parse_attr1([{_Id,_}|R],AttrList,Info, Res) -> + parse_attr1(R,AttrList,Info, Res); +parse_attr1([],Left,_, Res) -> + {lists:reverse(Res), Left}. + +attr_acc(#param{name=N}, List) -> + Name = list_to_atom(N), + case get_value(Name, List, undefined) of + undefined -> {undefined, List}; + Val -> {Val, lists:keydelete(Name,1,List)} + end. + +load_members(FileName, Class, Defs, Tab, Type,Opts) -> + File = filename:join(["wx_xml",FileName ++ ".xml"]), + put({loaded, FileName}, true), + case xmerl_scan:file(File, [{space, normalize}]) of + {error, enoent} -> + io:format("Skipped File not found ~p ~n", [File]), + Defs; + {Doc, _} -> + %% io:format("Scanning ~p ~n", [File]), + INCs = xmerl_xpath:string("./compounddef/includes/text()", Doc), + [put({include,reverse(tl(tl(reverse(Inc))))},ref) || + #xmlText{value=Inc} <- INCs], + case Type of + class -> + AM = xmerl_xpath:string("./compounddef/listofallmembers/*", Doc), + foldl(fun(X,Y) -> extract_rmembers(X,Y,Opts) end, Tab, AM); + _ -> + ignore + end, + LMembers0 = xmerl_xpath:string("./compounddef/sectiondef/*", Doc), + foldl(fun(E,Acc) -> extract_lmembers(E,Class,Type,Tab,Opts,Acc) end, Defs, LMembers0) + end. + +extract_lmembers(Entry=#xmlElement{name=memberdef,attributes=Attrs,content=C},Class,Type,Tab,Opts,Acc) -> + case keysearch(kind, #xmlAttribute.name, Attrs) of + {value, #xmlAttribute{value = "function"}} -> + case keysearch(prot, #xmlAttribute.name, Attrs) of + {value, #xmlAttribute{value = "public"}} -> + {value, #xmlAttribute{value = Id}} = + keysearch(id, #xmlAttribute.name, Attrs), + case Type of + static -> + Get = fun(#xmlElement{name=name,content=[#xmlText{value=Name}]},NAcc) -> + [name(string:strip(Name),Opts)|NAcc]; + (_D, NAcc) -> + NAcc + end, + case foldl(Get, [], C) of + [Name] -> + true = ets:insert(Tab,{Name,Id}); + [] -> + ignore + end; + _ -> ignore + end, + case gb_trees:lookup(Id,Acc) of + {value, _Entry} -> gb_trees:update(Id,Entry,Acc); + none -> gb_trees:insert(Id,Entry,Acc) + end; + _ -> + Acc + end; + {value, #xmlAttribute{value = "variable"}} when Type =/= static -> +%% {value, #xmlAttribute{value = Id}} = +%% keysearch(id, #xmlAttribute.name, Attrs), + %% Hopefully wxW have some decent order!! + Id = next_id(attr_id), + gb_trees:insert({attr,Id},Entry,Acc); + {value, #xmlAttribute{value = "enum"}} when Type =/= static -> + extract_enum(Entry,Class, undefined), + Acc; + _ -> Acc + + end. + +extract_rmembers(#xmlElement{name=member,attributes=Attrs,content=C},Tab, Opts) -> + {value,#xmlAttribute{value=Id}} = keysearch(refid, #xmlAttribute.name, Attrs), + Get = fun(#xmlElement{name=name,content=[#xmlText{value=Name}]},Acc) -> + [name(string:strip(Name),Opts)|Acc]; + (_D, Acc) -> + Acc + end, + case foldl(Get, [], C) of + [Name] -> + true = ets:insert(Tab,{Name,Id}); + [] -> + ignore + end, + Tab. + +select_member([{_,ID}], #class{name=Class,file=Orig}, Defs0, Opts) -> + [FileName, _8H|_] = string:tokens(ID, "_"), + case get({loaded, FileName}) =:= undefined + andalso get({loaded, FileName ++ "_" ++ _8H}) =:= undefined of + true -> + true = FileName =/= Orig, % Assert + Defs = load_members(FileName, Class, Defs0, skip, skip, Opts), + {[ID],Defs}; + false -> + {[ID],Defs0} + end; +select_member(Several, #class{name=Class,file=Orig}, Defs0, Opts) -> + MIds = [{string:tokens(MId, "_"),MId} || {_,MId} <- Several], + [StatFile |_ ] = string:tokens(Orig, "_"), + Check = + fun({[FN,_|_],ID}, {T,D}) when FN =:= Orig -> {[ID|T],D}; + ({[FN,"8h"|_],ID}, {T,D}) when FN =:= StatFile -> {[ID|T],D}; + ({[FN,_A|_],ID},{T,D}) -> + InBase = "class" ++ Class ++ "Base" =:= FN, + "wx" ++ ClassName = Class, + InGeneric = "classwxGeneric" ++ ClassName =:= FN, + IsHelper = case re:run(FN, "Helper$") of + {match,_} -> true; + _ -> false + end, + ImplBase = case re:run(FN, "Base$") of + {match,_} -> true; + _ -> + %% Hack for base-base class + FN =:= "classwxItemContainer" + end, + case InBase orelse InGeneric orelse IsHelper orelse ImplBase of + true -> + Defs = case get({loaded, FN}) of + undefined -> + true = FN =/= Orig, % Assert + load_members(FN,Class,D,skip,skip,Opts); + true -> D + end, + {[ID|T], Defs}; + _C -> + %% io:format("DBG ~p ~p ~p ~p ~n",[FN,_A,_C,Class]), + {T,D} + end + end, + foldl(Check,{[],Defs0},MIds). + +parse_member(Data,MType,Virtual,Opts = #hs{fopt=Fopts}) -> + Parse = fun(Con,A) -> parse_member2(Con,Opts,A) end, + Method = #method{name=MName,params=PS0} = + foldl(Parse, #method{method_type=MType, virtual=Virtual}, Data), + %% Skip motif name's if it's last and optional + PS2 = case PS0 of %% Backward order.. + [#param{name="name",def=Def,type=#type{name="wxString"}}|PS1] + when Def =/= none -> + PS1; + _ -> + PS0 + end, + Sz = length(PS2), + PS = map(fun(P=#param{name=PName}) -> + patch_param(MName,{Sz,PName},P,Fopts) + end, PS2), + Alias = find_erl_alias_name(MName,PS,Fopts), + Method#method{params=PS, alias=Alias}. + +find_erl_alias_name(MName,Ps,Fopts) -> + case gb_trees:lookup(MName, Fopts) of + {value, FuncO} when is_list(FuncO) -> + Aliases = lists:foldl(fun({Var, {erl_func, AliasName}}, Acc) -> + [{Var,AliasName}|Acc]; + ({erl_func, AliasName}, Acc) -> + [{all,AliasName}|Acc]; + ({Var, List}, Acc) when is_list(List) -> + case get_value(erl_func,List) of + undefined -> + Acc; + AliasName -> + [{Var,AliasName}|Acc] + end; + (_,Acc) -> Acc + end, [], FuncO), + case Aliases of + [] -> + undefined; + _ -> + Find = fun({all,AliasName},Acc) -> [AliasName|Acc]; + ({Var,AliasName},Acc) -> + case lists:keysearch(Var, #param.name, Ps) of + {value, _} -> [AliasName|Acc]; + _ -> Acc + end + end, + case lists:foldl(Find, [], Aliases) of + [Alias] -> Alias; + [] -> undefined + end + end; + _ -> + undefined + end. + +parse_member2(#xmlElement{name=type, content=C},Opts,M0) -> + Type = parse_type(drop_empty(C), Opts), + M0#method{type=Type}; +parse_member2(#xmlElement{name=name, content=[#xmlText{value=C}]}, Opts, M0) -> + Func = string:strip(C), + put(current_func, Func), + M0#method{name=name(Func,Opts)}; +parse_member2(#xmlElement{name=param, content=C},Opts,M0) -> + Parse = fun(Con, Ac) -> parse_param(Con, Opts, Ac) end, + Param0 = foldl(Parse, #param{}, drop_empty(C)), + add_param(Param0, Opts, M0); +parse_member2(_, _,M0) -> + M0. + +add_param(InParam, Opts, M0) -> + Param0 = case InParam#param.name of + undefined -> InParam#param{name="val"}; + _ -> InParam + end, + Param = case Param0#param.type of + #type{base={comp,_,_Comp}} -> Param0; + #type{base={class,_Class}} -> Param0; + #type{base={ref,_}} -> Param0; + #type{base={term,_}} -> Param0; + #type{base=List} when is_list(List) -> Param0; + %% Assume the pointer args to base types are out parameters + #type{by_val=false,single=true, mod=Mod} -> + case lists:member(const, Mod) of + true -> Param0; % But not if they are const + false -> Param0#param{in=false} + end; + _ -> Param0 + end, + add_param2(Param, Opts, M0). + +add_param2(P=#param{name=Name},#hs{fopt=FOpt},M0=#method{name=MName,params=Ps}) -> + case patch_param(MName, Name, P, FOpt) of + #param{where=nowhere} -> + M0#method{params=Ps}; + Patched -> + %% case MName of %% DEBUG + %% "GetSelections" -> + %% io:format("~p~n",[Patched]); + %% _ -> ignore + %% end, + %%ASSERT + case Patched#param.type of + #type{base=undefined} -> ?error({unknown_type,Patched}); + _ -> ok + end, + M0#method{params=[Patched|Ps]} + end. + +patch_param(Method, Name, P, Opt) -> + case gb_trees:lookup(Method,Opt) of + none -> P; + {value,NoArg} when is_integer(NoArg) -> P; + {value,Opts} when is_list(Opts) -> + case get_value(Name, Opts) of + undefined -> P; + List when is_list(List) -> + foldl(fun handle_param_opt/2,P,List); + Val -> + handle_param_opt(Val,P) + end + end. + +handle_param_opt(skip, P) -> P#param{where=c}; +handle_param_opt(nowhere, P) -> P#param{where=nowhere}; +handle_param_opt(skip_member, _P) -> throw(skip_member); +handle_param_opt({skip_member, Type}, P) -> + case P of + #param{type=#type{name=Type}} -> + throw(skip_member); + #param{type=Type} -> + throw(skip_member); + _ -> + P + end; +handle_param_opt({erl_func,_Name}, P) -> P; %% Handled elsewhere +handle_param_opt(in, P) -> P#param{in=true}; +handle_param_opt(out, P) -> P#param{in=false}; +handle_param_opt(both, P) -> P#param{in=both}; +handle_param_opt({def,Def},P) -> P#param{def=Def}; +handle_param_opt({type,Type}, P=#param{type=T}) -> P#param{type=T#type{name=Type}}; +handle_param_opt({single,Opt}, P=#param{type=T}) -> P#param{type=T#type{single=Opt}}; +handle_param_opt({base,Opt}, P=#param{type=T}) -> P#param{type=T#type{base=Opt}}; +handle_param_opt({c_only,Opt},P) -> P#param{where=c, alt=Opt}; +handle_param_opt({ref, pointer}, P=#param{type=T}) -> + P#param{type=T#type{by_val=false,ref={pointer, 1}}}; +handle_param_opt({mod,Mods}, P=#param{type=T=#type{mod=Mods0}}) -> + P#param{type=T#type{mod=Mods++Mods0}}. + +get_opt(Opt, Method, Sz, Opts) -> + case gb_trees:lookup(Method,Opts) of + none -> undefined; + {value, List} when is_list(List) -> + case get_value({Sz,Opt}, List, undefined) of + undefined -> + get_value(Opt, List, undefined); + Res -> Res + end + end. + +parse_param(#xmlElement{name=type,content=C},Opts,T) -> + Type = parse_type(drop_empty(C),Opts), + T#param{type=Type}; +parse_param(#xmlElement{name=declname,content=[C]},_Opts,T) -> + #xmlText{value=Name} = C, + T#param{name=Name}; +parse_param(#xmlElement{name=defval,content=[#xmlText{value=Def}]},_Opts,T) -> + T#param{def=string:strip(Def)}; +parse_param(#xmlElement{name=defval,content=Other},_Opts,T) -> + %% For defaults = (modifer wxType *) NULL + Def0 = foldr(fun(#xmlText{value=V}, Acc) -> V ++ Acc; + (#xmlElement{content=[#xmlText{value=V}]},Acc) -> + V ++ Acc + end, [], Other), +%% Def1 = lists:dropwhile(fun($)) -> false;(_) -> true end, Def0), +%% Def = string:strip(Def1), %% Drop type cast !! +%% io:format("Def ~s => ~s => ~s ~n", [Def0, Def1,string:strip(Def)]), + T#param{def=string:strip(Def0)}; +parse_param(#xmlElement{name=array,content=C},_Opts, T = #param{type=Type0}) -> + case Type0 of + _ when T#param.name=:="WXUNUSED" -> %% doxygen can't handle this macro + [#xmlText{value=RealVar}] = C, + [Name] = string:tokens(RealVar, "() "), + T#param{name=Name}; +%% #type{mod=[const]} -> +%% T#param{type=Type0#type{single=array, by_val=true}}; +%% _ -> +%% T#param{type=Type0#type{single=array, by_val=false}} + _ -> + T#param{type=Type0#type{single=array, by_val=true}} + end; +parse_param(#xmlElement{name=name,content=[C]}, _, T) -> + %% Attributes have this + #xmlText{value=Name} = C, + T#param{name=Name}; +%% Skipped: Attributes have this +parse_param(#xmlElement{name=definition}, _, T) -> T; +parse_param(#xmlElement{name=argsstring}, _, T) -> T; +parse_param(#xmlElement{name=briefdescription}, _, T) -> T; +parse_param(#xmlElement{name=detaileddescription}, _, T) -> T; +parse_param(#xmlElement{name=inbodydescription}, _, T) -> T; +parse_param(#xmlElement{name=location}, _, T) -> T; +parse_param(#xmlElement{name=referencedby}, _, T) -> T; +parse_param(Other=#xmlElement{name=Name}, _, T) -> + io:format("Unhandled Param ~p ~p ~n in ~p~n", [Name,Other,T]), + ?error(unhandled_param). + +parse_type([], _Opts) -> void; +parse_type(TypeInfo, Opts) -> + {Type,Info} = foldl(fun extract_type_info/2,{[],undefined},TypeInfo), + case Info of + {"member", Ref} -> + case string:tokens(Ref, "_") of + [FileName, "8h", _Id] -> + put({file_ref, FileName++"_8h"}, ref); + _ -> + ok + end; + _ -> ok + end, + + Empty = #type{}, + case parse_type2(reverse(Type),Info,Opts,#type{}) of + Empty -> ?error({strange_type, Type}); + Assert -> Assert + end. + +extract_type_info(#xmlText{value=Value}, {Acc, Info}) -> + {reverse(foldl(fun extract_type_info2/2, [], string:tokens(Value, " "))) ++ Acc, Info}; +extract_type_info(#xmlElement{name=ref,attributes=As,content=[#xmlText{value=V}]}, + {Acc,undefined}) -> + {value, #xmlAttribute{value = Refid}} = keysearch(refid,#xmlAttribute.name,As), + {value, #xmlAttribute{value = Kind}} = keysearch(kindref,#xmlAttribute.name,As), + {reverse(foldl(fun extract_type_info2/2, [], string:tokens(V, " "))) ++ Acc, + {Kind,Refid}}; +extract_type_info(What,Acc) -> + ?error({parse_error,What,Acc}). + +extract_type_info2("const",Acc) -> [const|Acc]; +extract_type_info2("*", [{by_ref,{pointer,N}}|Acc]) -> [{by_ref,{pointer,N+1}}|Acc]; +extract_type_info2("*", Acc) -> [{by_ref,{pointer,1}}|Acc]; +extract_type_info2("**", Acc) -> [{by_ref,{pointer,2}}|Acc]; +extract_type_info2("&", Acc) -> [{by_ref,reference}|Acc]; +extract_type_info2("WXDLLIMP" ++ _, Acc) -> Acc; +extract_type_info2(Type, Acc) -> [Type|Acc]. + +parse_type2(["void"], _Info, _Opts, _T) -> void; +parse_type2(["virtual"|R], _Info, _Opts, _T) -> + [] = R, + %% Bug in old doxygen virtual destructors have type virtual + void; +parse_type2(["wxe_cb"|R],Info,Opts, T) -> + parse_type2(R,Info,Opts,T#type{name=int,base=wxe_cb}); +parse_type2([const|R],Info,Opts,T=#type{mod=Mod}) -> + parse_type2(R,Info,Opts,T#type{mod=[const|Mod]}); +parse_type2(["unsigned"|R],Info,Opts,T=#type{mod=Mod}) -> + parse_type2(R,Info,Opts,T#type{mod=[unsigned|Mod]}); +parse_type2(["int"|R],Info,Opts, T) -> + parse_type2(R,Info,Opts,T#type{name=int,base=int}); +parse_type2(["char"|R],Info,Opts, T) -> + parse_type2(R,Info,Opts,T#type{name="char",base=int}); +parse_type2([N="size_t"|R], Info, Opts, T) -> + parse_type2(R,Info,Opts,T#type{name=N, base=int}); +parse_type2(["long"|R],Info, Opts, T) -> + parse_type2(R,Info,Opts,T#type{name=long,base=int}); +parse_type2(["float"|R],Info,Opts, T) -> + parse_type2(R,Info,Opts,T#type{name=float,base=float}); +parse_type2(["double"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=double,base=double}); +parse_type2([N="wxDouble"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=double}); +parse_type2(["bool"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=bool,base=bool}); +parse_type2([N="wxWindowID"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=int}); +parse_type2([N="wxTextCoord"|R],Info,Opts,T) -> %%long + parse_type2(R,Info,Opts,T#type{name=N,base=int}); +parse_type2([N="wxTextPos"|R],Info,Opts,T) -> %%long + parse_type2(R,Info,Opts,T#type{name=N,base=int}); +parse_type2([N="wxPrintQuality"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=int}); +parse_type2([N="wxPaperSize"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=int}); +parse_type2(["wxDataFormat"|_R],_Info,_Opts,T) -> + %% Hack Hack + T#type{name="wxDataFormatId",base=int}; +parse_type2([N="wxArrayInt"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=int,single=array}); +parse_type2([N="wxArrayDouble"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=double,single=array}); +parse_type2([N="wxTreeItemId"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base={ref,N}}); +parse_type2([N="wxArrayTreeItemIds"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base={ref,"wxTreeItemId"},single=array}); +parse_type2([N="wxTreeItemData"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name="wxETreeItemData",base={term,N}}); +parse_type2([N="wxClientData"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name="wxeErlTerm",base={term,N}}); +parse_type2([N="wxChar"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=int}); +parse_type2(["wxUint32"|R],Info,Opts,T=#type{mod=Mod}) -> + parse_type2(R,Info,Opts,T#type{name=int,base=int,mod=[unsigned|Mod]}); +parse_type2([N="wxCoord"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=int}); +parse_type2([N="wxPoint"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base={comp,N,[{int,"X"},{int,"Y"}]}}); +parse_type2([N="wxSize"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base={comp,N,[{int,"W"},{int,"H"}]}}); +parse_type2([N="wxGBPosition"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base={comp,N,[{int,"R"},{int,"C"}]}}); +parse_type2([N="wxGBSpan"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base={comp,N,[{int,"RS"},{int,"CS"}]}}); +parse_type2([N="wxGridCellCoords"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base={comp,N,[{int,"R"},{int,"C"}]}}); +parse_type2([N="wxGridCellCoordsArray"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base={comp,"wxGridCellCoords", + [{int,"R"},{int,"C"}]}, + single=array}); +parse_type2([N="wxRect"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base={comp,N,[{int,"X"},{int,"Y"}, + {int,"W"},{int,"H"}]}}); +parse_type2([N="wxColour"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N, + base={comp,N,[{int,"R"},{int,"G"},{int,"B"},{int,"A"}]}}); +parse_type2([N="wxColor"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name="wxColour", + base={comp,N,[{int,"R"},{int,"G"},{int,"B"},{int,"A"}]}}); + +parse_type2([N="wxPoint2DDouble"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N, + base={comp,N,[{double,"X"},{double,"Y"}]}}); +parse_type2([N="wxRect2DDouble"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N, + base={comp,N,[{double,"X"},{double,"Y"}, + {double,"W"},{double,"H"}]}}); + +parse_type2([N="wxDateTime"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N, + base={comp,N,[{int,"D"},{int,"Mo"},{int,"Y"}, + {int,"H"},{int,"Mi"},{int,"S"}]} + }); + +parse_type2([N="wxMouseState"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N, base={comp,N,{record, wxMouseState}}}); +parse_type2([N="wxHtmlLinkInfo"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N, base={comp,N,{record, wxHtmlLinkInfo}}}); +parse_type2([N="wxString"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=string}); +parse_type2([N="wxArtClient"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=string}); +parse_type2(["wxArtID"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name="wxString",base=string}); +parse_type2([N="wxFileName"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=string}); +parse_type2([N="wxArrayString"|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=[int],single=array,by_val=true}); +parse_type2([{by_ref,Ref}|R],Info,Opts,T) -> + parse_type2(R,Info,Opts,T#type{ref=Ref,by_val=false}); +parse_type2([],_,_,T) -> T; + +parse_type2([N="wxImageList"|R],Info,Opts,T) -> %% ARRG breaks the following clause + parse_type2(R,Info,Opts,T#type{name=N,base={class,N}}); +parse_type2(L=[Name|R],I,Opts,T) -> + case reverse(Name) of + "tsiL" ++ RBase -> + parse_type2(R,I,Opts, + T#type{name=Name,base={class,reverse(RBase)},single=list}); + _ -> + parse_type3(L,I,Opts,T) + end. + +parse_type3(["wxNotebookPage"|R],I,Opts,T) -> + Xml = case I of + {_, Ref} -> Ref; + undefined -> undefined + end, + parse_type2(R,I,Opts,T#type{name="wxWindow",base={class,"wxWindow"},xml=Xml}); +parse_type3([N|R],I={"member",Ref},Opts,T) -> + Type = name(N,Opts), + ErlClass = special_name(Type), + case string:tokens(Ref, "_") of + ["class" ++ _] -> ignore; + Other -> + Inc0 = lists:takewhile(fun("8h") -> false;(_) -> true end,Other), + Inc = gen_util:args(fun(A) -> A end, "_", Inc0), +%% io:format("Inc ~s ~n", [Inc]), + put({include,Inc}, ref) + end, + case get_enum(Type) of + {_, undefined} -> + parse_type2(R,I,Opts,T#type{name=Type,base={class,ErlClass},xml=Ref}); + {TypeWOClass,#enum{}} -> + parse_type2(R,I,Opts,T#type{name=Type,base={enum,TypeWOClass},xml=Ref}) + end; +parse_type3([N = "wx"++_|R],I,Opts,T) -> + Xml = case I of + {_, Ref} -> Ref; + undefined -> undefined + end, + Class = name(N,Opts), + ErlClass = special_name(Class), + parse_type2(R,I,Opts,T#type{name=Class,base={class,ErlClass},xml=Xml}); +parse_type3([N="WXWidget"|R], Info,Opts, T) -> + parse_type2(R,Info,Opts,T#type{name=N,base=long}); +%% Let type errors be seen later because we don't know if these unhandled types +%% will be used. +parse_type3([Name|R],Info,Opts, T) -> + New = T#type{name={unhandled,Name,Info,get(current_class),get(current_func)}}, + parse_type2(R,Info,Opts, New). + +%%skipped(#method{method_type=constructor, type=void}, _Opts) -> true; +skipped(#method{}, #hs{skip=[]}) -> false; +skipped(#method{name=Name,params=P}, #hs{skip=Skip}) -> + AtomName = list_to_atom(Name), + Skipped = lists:member(AtomName, Skip) orelse + lists:member({AtomName,length(P)}, Skip), + %% io:format("~p ~p skipped ~p ~n", [AtomName, length(P),Skipped]), + Skipped. + +add_method(Ms0, NoArgs, Class, Opts) -> + Add = fun(M=#method{params=Ps0}, Acc) -> + case length(Ps0) of + NoArgs -> + [add_method2(M,Class,Opts)|Acc]; + _ when NoArgs =:= all -> + [add_method2(M,Class,Opts)|Acc]; + _ -> + Acc + end + end, + NewMs = lists:foldl(Add,[],Ms0), + Unique = filter_functions(reverse(NewMs), Opts), + erase(current_func), + foldl(fun(M,C=#class{methods=Ms}) when is_list(M) -> C#class{methods=[M|Ms]} end, + Class,reverse(Unique)). + +add_method2(M0=#method{name=Name,params=Ps0,type=T0},#class{name=CName,parent=Par},#hs{fopt=Opts}) -> + Type = case patch_param(Name, return, #param{type=T0}, Opts) of + #param{type = T0} -> + case patch_param(Name, {length(Ps0),return}, #param{type=T0}, Opts) of + #param{where=nowhere} -> void; + #param{type = Type0} -> Type0 + end; + #param{where=nowhere} -> void; + #param{type = Type0} -> Type0 + end, + + {Req,Opt} = lists:partition(fun(#param{def=Def}) -> Def == none end, + M0#method.params), + Ps = reverse(Ps0), + + IsStatic = case Par of + "static" -> static; + _ -> M0#method.method_type + end, + Where = case get_opt(where, Name, length(Ps), Opts) of + undefined -> both; + Other -> + Other + end, + M1 = M0#method{defined_in=CName, + min_arity = length(Req), + max_arity = length(Req) + if length(Opt) > 0 -> 1; true -> 0 end, + type = Type, + method_type = IsStatic, + where = Where, + id=next_id(func_id), + pre_hook = get_opt(pre_hook, Name, length(Ps), Opts), + post_hook = get_opt(post_hook, Name, length(Ps), Opts), + doc = get_opt(doc, Name, length(Ps), Opts) + }, + M = case Name of + CName -> + M1#method{method_type=constructor,name=CName, + type=constructor(CName), params=Ps}; + [$~|CName] -> + M1#method{method_type=destructor,name=Name, + params=[this(CName)|Ps]}; + _ -> + case M1#method.method_type of + static -> M1#method{params=Ps}; + member -> M1#method{params=[this(CName)|Ps]} + end + end, + M. + +this(Class) -> + #param{name="This",where=this, + type=#type{name=Class,base={class,Class},by_val=false,ref={pointer,1}}}. + +constructor(Class) -> + #type{name=Class,base={class,Class},by_val=false,ref=reference}. + +filter_functions(Parsed, Opts) -> + Left = foldl(fun(M0,Acc) -> + case skipped(M0, Opts) of + true -> Acc; + false -> + TF = extract_type_footprint(M0), + [TF|Acc] + end + end,[],Parsed), + Clean = remove_or_merge(lists:sort(Left),[],[]), + erl_skip_opt(reverse(Clean),[],[]). + +remove_or_merge([{A,{L,In,O1},M1}|Rest=[{A,{L,In,O2},M2}|_]],Acc1,Acc2) + when M1#method.method_type =:= M2#method.method_type -> + %% These are the same remove one of them. + case O1 =:= O2 of + true -> ok; + false -> + ?warning("Multiple out arguments of ~s:~s: ~p or ~p~n", + [get(current_class),M1#method.name, O1,O2]) + end, + remove_or_merge(Rest,Acc1,Acc2); +remove_or_merge([F={A,{Len,_,_},M1}|Rest],[{A,{Len,_,_},M2}|_]=Acc1,Acc2) + when M1#method.method_type =:= M2#method.method_type -> + NewAcc1 = maybe_merge(F,Acc1,[]), + remove_or_merge(Rest,NewAcc1,Acc2); +remove_or_merge([F|Rest],[],Acc2) -> + remove_or_merge(Rest,[F],Acc2); +remove_or_merge([F|Rest],Acc1,Acc2) -> + remove_or_merge(Rest,[F], [reverse(Acc1)|Acc2]); +remove_or_merge([],[], Acc2) -> Acc2; +remove_or_merge([],Acc1,Acc2) -> [reverse(Acc1)|Acc2]. + +erl_skip_opt([Ms|R],[],Acc2) -> + {Orig, Skipped} = erl_skip_opt2(Ms,[],[],[]), + erl_skip_opt(R,Orig,[Skipped|Acc2]); +erl_skip_opt(All=[Ms=[{_,{Len,_,_},_}|_]|R],Acc1=[{_,{N,_,_},_}|_], Acc2) -> + case Len =:= N+1 of + true -> + {Orig, Skipped} = erl_skip_opt2(Ms,[],[],Acc1), + erl_skip_opt(R,Orig,[Skipped++strip_ti(Acc1)|Acc2]); + false -> + erl_skip_opt(All, [], [strip_ti(Acc1)|Acc2]) + end; +erl_skip_opt([],Acc1,Acc2) -> [strip_ti(Acc1)|Acc2]. + +erl_skip_opt2([F={_,{N,In,_},M=#method{where=Where}}|Ms],Acc1,Acc2,Check) -> + case N > 0 andalso lists:last(In) =:= opt_list of + true when Where =/= merged_c, Where =/= taylormade -> + case Check of + [] -> + erl_skip_opt2(Ms,[F|Acc1],[M#method{where=erl_no_opt}|Acc2],[]); + _ -> + Skipped = reverse(tl(reverse(In))), + T = fun({_,{_,Args,_},_}) -> true =:= types_differ(Skipped,Args) end, + case lists:all(T, Check) of + true -> + erl_skip_opt2(Ms,[F|Acc1], + [M#method{where=erl_no_opt}|Acc2], + Check); + false -> + erl_skip_opt2(Ms,[F|Acc1],Acc2,Check) + end + end; + _ -> + erl_skip_opt2(Ms,[F|Acc1],Acc2,[]) + end; +erl_skip_opt2([],Acc1,Acc2,_) -> {Acc1,Acc2}. + +strip_ti(Ms) -> + [M || {_,{_,_,_},M} <- Ms]. + +maybe_merge(T1,[],Acc) -> reverse([T1|Acc]); +maybe_merge(F={A1,T1={Len,In1,O1},M1},[C={A2,T2={Len,In2,O2},M2}|Rest],Acc) -> + case types_differ(In1,In2) of + true -> maybe_merge(F,Rest,[C|Acc]); + {class,C1,C2} when O1 =:= O2 -> + {Merged,M2Mod} = merge_class_params(M1,M2,C1,C2), + reverse([{A1,T1,Merged},{A2,T2,M2Mod}|Acc]) ++ Rest; + false -> + ?warning("Argument clash in ~s:~s:~n ~p~nor ~p~n", + [get(current_class),M1#method.name,{In1,O1},{In2,O2}]), + [F|Rest++Acc] + end. + +merge_class_params(M1=#method{params=P1,id=Mi1},M2=#method{params=P2,id=Mi2},C1,C2) -> + Merged = merge_class_params2({class,C1},P1,Mi1,{class,C2},P2,Mi2), + {M1#method{params=Merged}, M2#method{where=merged_c}}. + +merge_class_params2(B1,[P1|R1],M1,B2,[P1|R2],M2) -> + [P1|merge_class_params2(B1,R1,M1,B2,R2,M2)]; +merge_class_params2(B1,[P1=#param{type=T1=#type{base=B1}}|R1],M1, + B2,[#param{type=T2=#type{base=B2}}|R2],M2) -> + [P1#param{type={merged,M1,T1,R1,M2,T2,R2}}|merge_class_params2(B1,R1,M1,B2,R2,M2)]; +merge_class_params2(B1,[P1|R1],_M1,B2,[P2|R2],_M2) -> + io:format("Merged Failed ~p ~p~n", [B1,B2]), + io:format(" ~p ~p~n ~p~p~n", [P1,R1,P2,R2]), + ?error(merged_failed); +merge_class_params2(_,[],_,_,[],_) -> + []. + +types_differ([C1|R1], [C2|R2]) when is_list(C1), is_list(C2) -> + types_differ(R1,R2); %% Different Classes +types_differ([C|R1], [C|R2]) -> + types_differ(R1,R2); +types_differ([{term,_}|R1], [_|R2]) -> + types_differ(R1,R2); +types_differ([_|R1], [{term,_}|R2]) -> + types_differ(R1,R2); +types_differ([{class,C1}|R1], [{class,C2}|R2]) -> + case types_differ(R1,R2) of + true -> + true; + false -> +%% _ -> + {class,C1,C2}; + {class,C1,C2} -> + {class,C1,C2}; + {class, _,_} -> + false + end; +types_differ([int|_], _) -> true; +types_differ(_, [int|_]) -> true; +types_differ([{class,_}|_], _) -> true; +types_differ(_, [{class,_}|_]) -> true; +types_differ([binary|_], _) -> true; +types_differ(_, [binary|_]) -> true; + +types_differ([list|R1], [opt_list|R2]) -> + types_differ(R1,R2); +types_differ([opt_list|R1], [list|R2]) -> + types_differ(R1,R2); +types_differ([C1|R1], [C2|R2]) when is_tuple(C1), is_tuple(C2) -> + (size(C1) =/= size(C2)) orelse types_differ(R1,R2); +types_differ([C1|_R1], [_C2|_R2]) when is_tuple(C1) -> + true; +types_differ([_C1|_R1], [C2|_R2]) when is_tuple(C2)-> + true; +types_differ([_C1|R1], [_C2|R2]) -> %% More cases? + types_differ(R1,R2); +types_differ([], []) -> + false. + +extract_type_footprint(M=#method{type=void,alias=A,params=Ps}) -> + {A,extract_type_footprint2(Ps, [], [], false), M}; +extract_type_footprint(M=#method{type=Type,alias=A,params=Ps}) -> + {A,extract_type_footprint2(Ps, [type_foot_print(Type)], [], false), M}. + +extract_type_footprint2([_P=#param{where=c, in=InArg}|R], Out, In, Opt) + when InArg =/= false -> + extract_type_footprint2(R, Out, In, Opt); +extract_type_footprint2([_P=#param{def=Def, in=InArg}|R], Out, In, _Opt) when Def =/= none, InArg =/= false -> + extract_type_footprint2(R, Out, In, true); +extract_type_footprint2([#param{in=false, type=Type}|Ps], Out, In, Opt) -> + extract_type_footprint2(Ps, [type_foot_print(Type)|Out], In, Opt); +extract_type_footprint2([#param{in=true, type=Type}|Ps], Out, In, Opt) -> + extract_type_footprint2(Ps, Out, [type_foot_print(Type)|In], Opt); +extract_type_footprint2([#param{in=both, type=Type}|Ps], Out, In, Opt) -> + TFP = type_foot_print(Type), + extract_type_footprint2(Ps, [TFP|Out], [TFP|In], Opt); + +extract_type_footprint2([], Out0, In, Opt) -> + Out = case Out0 of + [] -> void; + [One] -> One; + _ -> list_to_tuple(reverse(Out0)) + end, + if Opt -> + {length(In)+1,reverse([opt_list|In]),Out}; + true -> + {length(In), reverse(In),Out} + end. + +type_foot_print(#type{single=Single}) when Single =/= true -> list; +type_foot_print(#type{base=string}) -> list; +type_foot_print(#type{base=Base}) when is_list(Base) -> list; +type_foot_print(#type{base=long}) -> int; +type_foot_print(#type{base=binary}) -> binary; +type_foot_print(#type{base={binary,_}}) -> binary; +type_foot_print(#type{base=int}) -> int; +type_foot_print(#type{base=bool}) -> bool; +%%type_foot_print(#type{base=datetime}) -> datetime; +type_foot_print(#type{base=float}) -> float; +type_foot_print(#type{base=double}) -> float; +type_foot_print(#type{base=C={class,_}}) -> C; +type_foot_print(#type{base={enum,_}}) -> int; +type_foot_print(#type{base={ref,_}}) -> ref; +type_foot_print(#type{base={term,_}}) -> term; +type_foot_print(#type{base=eventType}) -> atom; +%% type_foot_print({Type,Str}) when is_list(Str) -> +%% type_foot_print(Type); +type_foot_print(#type{base={comp,_,R={record,_}}}) -> + R; +type_foot_print(#type{base={comp,_,Types}}) -> + TFL = map(fun({T,N}) when is_list(N) -> + case T of + double -> float; + _ -> T + end + end, Types), + list_to_tuple(TFL). +%type_foot_print(What) -> What. + + +translate_enums(Defs) -> + Res = [translate_enums1(Def) || Def <- Defs], + Consts = [Enum || Enum = {{enum,_},_} <- get()], + translate_constants(Consts, get(not_const), get(const_skip)), + put(gvars, [{Gvar,Class,next_id(const)} || {Gvar,Class} <- lists:sort(get(gvars))]), + Res. + +translate_enums1(C=#class{name=Name, methods=Ms0, attributes=As0}) -> + Ms = [translate_enums2(M, Name) || M <- Ms0], + As = [translate_enums3(A, Name) || A <- As0], + C#class{methods=Ms, attributes=As}. + +translate_enums2(M=#method{params=Ps0, type=T0},Class) -> + Ps = [translate_enums3(P, Class) || P <- Ps0], + T = translate_enums_type(T0,Class), + M#method{params=Ps,type=T}; +translate_enums2(Ms,Class) when is_list(Ms) -> + [translate_enums2(M,Class) || M <- Ms]. + +translate_enums3(P=#param{type=Type0},InClass) -> + Type = translate_enums_type(Type0,InClass), + P#param{type=Type}; +translate_enums3(InHer = {inherited, _},_InClass) -> + InHer. + +translate_enums_type(T=#type{base={class,C}},Class) -> + case get_enum(C,Class) of + {_, undefined} -> T; + {Enum, #enum{}} -> + %% io:format("ENUM Change class ~p to enum ~p~n", [C,Enum]), + T#type{base={enum, Enum}} + end; +translate_enums_type(T,_Class) -> T. + +translate_constants(Enums, NotConsts0, Skip0) -> + NotConsts = gb_sets:from_list(NotConsts0), + Skip = gb_sets:from_list(Skip0), + Consts0 = create_consts(lists:sort(Enums), Skip, NotConsts, []), + put(consts, gb_trees:from_orddict(lists:ukeysort(1,[{N,C}|| C = #const{name=N} <- Consts0]))). + +create_consts([{{enum, Name},Enum = #enum{vals=Vals}}|R], Skip, NotConsts, Acc0) -> + CC = fun(What, Acc) -> + create_const(What, Skip, NotConsts, Acc) + end, + Acc = case Vals of + undefined -> + ?warning("Missing Enum ~p ~p ~n",[Name, Enum]), + Acc0; + [] -> %% ?warning("Ignored Empty Enum list ~p ~n",[_Name]), + Acc0; + _ -> + foldl(CC, Acc0, lists:sort(Vals)) + end, + create_consts(R, Skip, NotConsts, Acc); +create_consts([],_,_,Acc) -> Acc. + +create_const({Name, Val}, Skip, NotConsts, Acc) -> + case gb_sets:is_member(Name, Skip) of + true -> Acc; + false -> + case gb_sets:is_member(Name, NotConsts) of + true -> + [#const{name=Name,val=next_id(const),is_const=false}|Acc]; + false -> + [#const{name=Name,val=Val,is_const=true}|Acc] +%% false -> +%% [#const{name=Name,val=Val}|Acc] + end + end. + +%%%%%%%%%%%%% +next_id(What) -> + Next = case get(What) of + undefined -> 100; + N -> N+1 + end, + put(What, Next), + Next. + +name([$~|Name], Opts) -> + [$~|name(Name,Opts)]; +name(Name0, #hs{alias=Alias}) -> + Name = case reverse(Name0) of + "esaBlooT" ++ _ -> %% Arrg uses base + Name0; + "esaBelbaTdirG" ++ _ -> %% Arrg uses base + Name0; + "esaBrekciP" ++ _ -> %% Arrg uses base + Name0; + "esaB" ++ Rest when hd(Name0) == $w -> + %% Arrg Some decl uses base class directly + reverse(Rest); + _F -> + Name0 + end, + get_value(Name,Alias,Name). + +special_name("wxIconLocation") -> "wx"; +special_name("wxToolBarToolBase") -> "wx"; +special_name("wxObject") -> "wx"; +special_name("wxValidator") -> "wx"; % XXXXX +%% special_name("wxTreeItemData") -> "wx"; % XXXXX +%% special_name("wxTreeItemId") -> "wx"; +%% special_name("wxDataObject") -> "wx"; +special_name(Other) -> Other. + +drop_empty(List) -> + filter(fun(#xmlText { value = Text}) -> + string:strip(Text) =/= ""; + (_)-> + true + end, List). + +%%% Enums +parse_enums(Files) -> + DontSearch = ["wxchar","filefn", "platform", "strconv", "filename", + "buffer", "string", "debug", "platinfo"], + %% Arg need to patch some specials, atleast for wx-2.6 + ExtraSearch = ["gtk_2glcanvas", "generic_2splash"], + parse_enums(Files ++ ExtraSearch,gb_sets:from_list(DontSearch)). + +parse_enums([File|Files], Parsed) -> + case gb_sets:is_member(File,Parsed) of + false -> + FileName = filename:join(["wx_xml",File ++ "_8h.xml"]), +%% io:format("Parse Enums in ~s ~n", [FileName]), + case xmerl_scan:file(FileName, [{space, normalize}]) of + {error, enoent} -> + parse_enums(Files, gb_sets:add(File,Parsed)); + {Doc, _} -> + ES = "./compounddef/sectiondef/memberdef[@kind=\"enum\"]", + AM = xmerl_xpath:string(ES, Doc), + lists:foreach(fun(Def) -> extract_enum(Def, undefined, File) end, AM), + + DS = "./compounddef/sectiondef/memberdef[@kind=\"define\"]", + Defs = xmerl_xpath:string(DS, Doc), + extract_defs(Defs,File), + + INCs = xmerl_xpath:string("./compounddef/includes/text()", Doc), + New = [reverse(tl(tl(reverse(Inc)))) || + #xmlText{value="wx/"++Inc} <- INCs], + %% io:format("Scan enums from ~p ~n", [File]), + parse_enums(New ++ Files, gb_sets:add(File,Parsed)) + end; + true -> + parse_enums(Files,Parsed) + end; +parse_enums([],_) -> ok. + +extract_enum(#xmlElement{name=memberdef,content=C}, Class, File) -> + {Name0,Vals0} = extract_enum2(C,undefined,0,[]), + {Vals,Name} = + if + hd(Name0) =:= $@, Class =:= undefined -> + {Vals0, Name0 ++ "_" ++ File}; + Class =:= undefined-> + {Vals0, Name0}; + true -> + {[{Class++"::"++N,V} || {N,V} <- Vals0], {Class,Name0}} + end, + case get({enum, Name}) of + undefined -> +%% io:format("1Enum name ~p~n", [Name]), +%% [io:format(" ~s ~p~n", [D,V]) || {D,V} <- Vals], + put({enum, Name}, #enum{vals=Vals}); + E = #enum{vals=undefined} -> +%% io:format("2Enum name ~p~n", [Name]), +%% [io:format(" ~s ~p~n", [D,V]) || {D,V} <- Vals], + put({enum, Name}, E#enum{vals=Vals}); + #enum{vals=Vals} -> ok; +%% io:format("Same? ~p ~n", [PVals == Vals]) + #enum{vals=OldVals} -> + io:format("Enum ~p in ~p ~p ~p~n", [Name,Class,get(current_class),get(current_func)]), + io:format("New ~p~n", [Vals]), + io:format("Old ~p~n", [OldVals]), + erlang:error({enum_mismatch,Name,Vals,OldVals}) + end, + ok. + +extract_enum2([#xmlElement{name=name,content=[#xmlText{value=Name}]}|R],_,Id,Acc0) -> + extract_enum2(R,Name,Id,Acc0); + +extract_enum2([#xmlElement{name=enumvalue,content=C}|R], N,Id,Acc0) -> + {Acc,NewId} = extract_enum3(C,Id,Acc0), + extract_enum2(R, N, NewId, Acc); +extract_enum2([_|R], N, Id, Acc) -> + extract_enum2(R, N, Id, Acc); +extract_enum2([], N, _Id, Acc) -> + {N, reverse(Acc)}. + +extract_enum3([#xmlElement{name=name,content=[#xmlText{value=Name}]}|R], Id, Acc) -> + case lists:keymember(Name, 1, Acc) of + true -> %% Doxygen double includes some defs. + {Acc,Id}; + false -> + case Id of + This = {Str,Num} -> + extract_enum3(R, {Str, Num+1}, [{Name,This}|Acc]); + Val -> + extract_enum3(R, Val+1, [{Name,Val}|Acc]) + end + end; + +extract_enum3([#xmlElement{name=initializer, + content=Cs=[#xmlText{}|_]}|_],_Id,[{Name,_}|Acc]) -> + + String = lists:append([string:strip(C#xmlText.value) || C <- Cs]), + + Val0 = gen_util:tokens(String,"<& "), + + try + case Val0 of + ["0x" ++ Val1] -> + Val = http_util:hexlist_to_integer(Val1), + {[{Name, Val}|Acc], Val+1}; + [Single] -> + Val = list_to_integer(Single), + {[{Name, Val}|Acc], Val+1}; + ["1", "<<", Shift] -> + Val = 1 bsl list_to_integer(Shift), + {[{Name, Val}|Acc], Val+1}; + [_Str, "+", _What] -> + Val = lists:append(Val0), + {[{Name, {Val, 0}}|Acc], {Val,1}}; + _What -> + %% io:format("~p Name ~p ~p~n",[?LINE, Name, Val0]), + throw(below) + end + catch _:_ -> + {[{Name,{String,0}}|Acc], {String,1}} + end; +extract_enum3([_|R], Id, Acc) -> + extract_enum3(R, Id, Acc); +extract_enum3([], Id, Acc) -> + {Acc, Id}. + +extract_defs(Defs, File) -> + case foldl(fun extract_defs2/2, {[], gb_sets:empty()}, Defs) of + [] -> ok; + {Vals,_Skip} -> +%% io:format("Defs file ~p~n", [File]), +%% [io:format(" ~s ~p~n", [D,V]) || {D,V} <- Vals, not is_integer(V)] + put({enum, {define,"From " ++ File ++ ".h"}}, #enum{vals=Vals}) + end. + +extract_defs2(#xmlElement{name=memberdef,content=C},{Acc,Skip}) -> + try + Res = {Name,_} = extract_def(C,undefined,Skip), + case gb_sets:is_member(Name,Skip) orelse lists:keymember(Name, 1, Acc) of + true -> {Acc,Skip}; + false -> {[Res | Acc], Skip} + end + catch throw:SkipName -> {Acc, gb_sets:add(SkipName,Skip)} + end. + +extract_def([#xmlElement{name=name,content=[#xmlText{value=Name}]}|R], _N, Skip) -> + case Name of + "wxUSE" ++ _ -> + throw(Name); + "wx" ++ _ -> + extract_def(R, Name, Skip); + _ -> + throw(Name) + end; +extract_def([#xmlElement{name=param}|_],Name,_) -> + throw(Name); +extract_def([#xmlElement{name=initializer,content=[#xmlText{value=Val0}]}|_],N,Skip) -> + case Val0 of + "0x" ++ Val1 -> {N, http_util:hexlist_to_integer(Val1)}; + _ -> + try + Val = list_to_integer(Val0), + {N, Val} + catch _:_ -> + case def_is_ok(Val0, Skip) of + false -> + throw(N); + NVal when is_integer(NVal) -> + {N, NVal}; + NVal -> + {N, {NVal,0}} + end + end + end; +extract_def([_|R],N,Skip) -> + extract_def(R,N,Skip); +extract_def(_,N,_) -> + throw(N). + +def_is_ok(Name, Skip) -> + Toks = gen_util:tokens(Name,"()| \\:"), + R = def_is_ok(Toks, Skip, []), +% io:format("~s -> ~p~n", [Name,R]), + R. + +def_is_ok([], _Skip, [")",Int, "("]) -> Int; +def_is_ok([], _Skip, Acc) -> lists:append(reverse(Acc)); +def_is_ok([N="wx"++_|R],Skip,Acc) -> + case gb_sets:is_member(N,Skip) of + true -> false; + false -> def_is_ok(R,Skip,[N|Acc]) + end; +def_is_ok(["0x"++Val|R],Skip,Acc) -> + def_is_ok(R,Skip,["16#" ++ Val|Acc]); +def_is_ok([N="|"|R], Skip, Acc) -> + def_is_ok(R,Skip,[N|Acc]); +def_is_ok([N="("|R], Skip, Acc) -> + def_is_ok(R,Skip,[N|Acc]); +def_is_ok([N=")"|R], Skip, Acc) -> + def_is_ok(R,Skip,[N|Acc]); +def_is_ok([":"|_], _Skip, _Acc) -> + false; +def_is_ok([N|R],Skip,Acc) -> + case catch list_to_integer(N) of + {'EXIT', _} -> false; + Int -> def_is_ok(R,Skip,[Int|Acc]) + end. + +get_enum(Type0) when is_list(Type0) -> + case string:tokens(Type0,":") of + [Type] -> + {Type, get({enum,Type})}; + [Class,Type] -> + get_enum(Type,Class) + end; +get_enum({Class,Type}) -> + get_enum(Type,Class). + +get_enum(Type,Class) -> + case get({enum,Type}) of + undefined -> + {{Class,Type},get({enum, {Class,Type}})}; + Res = #enum{} -> + {Type,Res} + end. |