<commit_before>%%
%% @copyright 2006-2007 <NAME>
%% @Copyright 2014 <NAME>
%%
%% @author <NAME> <<EMAIL>>
%% @author <NAME> <<EMAIL>.
%% @doc introspect support module
%%
-module(dbus_introspect).

-include_lib("xmerl/include/xmerl.hrl").
-include("dbus.hrl").
-include("dbus_errors.hrl").

-export([
	 find_interface/2,
	 find_method/3,
	 find_signal/3,
	 to_xml/1,
	 from_xml/1,
	 from_xml_string/1
	]).

-record(state, {node         :: dbus_node(),
		child        :: dbus_node(),
		iface        :: dbus_iface(),
		method       :: dbus_method(),
		signal       :: dbus_signal(),
		property     :: dbus_property(),
		s     = root :: atom()}).

%%%
%%% API
%%%
-spec find_interface(dbus_node(), Iface :: dbus_name()) -> 
			 {ok, dbus_iface()} | {error, dbus_err()}.
find_interface(#dbus_node{interfaces=Ifaces}, IfaceName) ->
    case find_name(IfaceName, Ifaces, fun dbus_names:bin_to_iface/1) of
	{value, #dbus_iface{}=I} ->
	    {ok, I};
	none ->
	    {error, {'org.freedesktop.DBus.UnknownInterface', [IfaceName]}}
    end.


-spec find_method(dbus_node(), Iface :: dbus_name(), Method :: dbus_name()) -> 
			 {ok, dbus_method()} | {error, dbus_err()}.
find_method(#dbus_node{interfaces=Ifaces}=Node, IfaceName, MethodName) ->
    case find_name(IfaceName, Ifaces, fun dbus_names:bin_to_iface/1) of
	{value, #dbus_iface{methods=Methods}=_I} ->
	    case find_name(MethodName, Methods, fun dbus_names:bin_to_method/1) of
		{value, #dbus_method{}=Method} ->
		    {ok, Method};
		none ->
		    {error, {'org.freedesktop.DBus.UnknownMethod', {[MethodName], IfaceName, Node}}}
	    end;
	none ->
	    {error, {'org.freedesktop.DBus.UnknownInterface', [IfaceName]}}
    end.


-spec find_signal(dbus_node(), Iface :: dbus_name(), Signal :: dbus_name()) -> 
			 {ok, dbus_signal()} | {error, dbus_err()}.
find_signal(#dbus_node{interfaces=Ifaces}=Node, IfaceName, SignalName) ->
    case find_name(IfaceName, Ifaces, fun dbus_names:bin_to_iface/1) of
	{value, #dbus_iface{signals=Signals}} ->
	    case find_name(SignalName, Signals, fun dbus_names:bin_to_signal/1) of
		{value, #dbus_signal{}=Signal} ->
		    {ok, Signal};
		none ->
		    {error, {'org.freedesktop.DBus.UnknownSignal', {[SignalName], IfaceName, Node}}}
	    end;
	none ->
	    {error, {'org.freedesktop.DBus.UnknownInterface', [IfaceName]}}
    end.


to_xml(#dbus_node{}=Node) ->
    Prolog = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">",
    lists:flatten(xmerl:export_simple([to_xmerl(Node)], xmerl_xml, [{prolog, Prolog}])).

from_xml_string(Data) when is_binary(Data) ->
    Opts = [{event_fun, fun xml_event/3}, 
	    {event_state, #state{}},
	    skip_external_dtd],
    case xmerl_sax_parser:stream(Data, Opts) of
	{ok, #dbus_node{}=Node, _Rest} ->
	    Node;
	{_Tag, _Location, Reason, _EndTags, _State} ->
	    ?error("Error parsing introspection: ~p~n", [Reason]),
	    throw({error, parse_error})
    end.

from_xml(Filename) ->
    Opts = [{event_fun, fun xml_event/3}, 
	    {event_state, #state{}},
	    skip_external_dtd],
    case xmerl_sax_parser:file(Filename, Opts) of
	{ok, #dbus_node{}=Node, _Rest} ->
	    Node;
	{_Tag, _Location, Reason, _EndTags, _State} ->
	    ?error("Error parsing introspection: ~p~n", [Reason]),
	    throw({error, parse_error})
    end.

%%%
%%% Priv
%%%
find_name(Name, Tree, _) when is_atom(Name) ->
    case gb_trees:lookup(Name, Tree) of
	{value, Iface} -> {value, Iface};
	none -> gb_trees:lookup(atom_to_binary(Name, utf8), Tree)
    end;
find_name(Name, Tree, ToKnownFun) when is_binary(Name) ->
    case gb_trees:lookup(Name, Tree) of
	{value, Iface} -> {value, Iface};
	none -> 
	    case ToKnownFun(Name) of
		Bin when is_binary(Bin) -> none;
		Atom when is_atom(Atom) -> gb_trees:lookup(Atom, Tree)
	    end
    end.


to_xmerl(undefined) ->
    [];
to_xmerl(List) when is_list(List) ->
    lists:map(fun(Elem) -> to_xmerl(Elem) end, List);

to_xmerl(#dbus_node{}=Elem) ->
    {node,
     case list_to_binary(Elem#dbus_node.name) of
	 undefined ->
	     [];
	 Name ->
	     [{name, Name}]
     end,
     to_xmerl(Elem#dbus_node.elements) ++
     to_xmerl(Elem#dbus_node.interfaces)
    };

to_xmerl(#dbus_iface{}=Elem) ->
    {interface,
     [{name, list_to_binary(Elem#dbus_iface.name)}],
     to_xmerl(Elem#dbus_iface.methods) ++
     to_xmerl(Elem#dbus_iface.signals) ++
     to_xmerl(Elem#dbus_iface.properties)};

to_xmerl(#dbus_method{}=Elem) ->
    Result =
	case Elem#dbus_method.result of
	    none ->
		[];
	    undefined ->
		[];
	    Arg ->
		[to_xmerl(Arg)]
	end,
    {method,
     case list_to_binary(Elem#dbus_method.name) of
	    undefined ->
	        [];
	    Name ->
	        [{name, Name}]
     end,
     to_xmerl(Elem#dbus_method.args) ++ Result};

to_xmerl(#dbus_signal{}=Elem) ->
    Result =
	case Elem#dbus_signal.result of
	    none ->
		[];
	    undefined ->
		[];
	    Arg ->
		[to_xmerl(Arg)]
	end,
    {signal,
     case list_to_binary(Elem#dbus_signal.name) of
	    undefined ->
	        [];
	    Name ->
	        [{name, Name}]
     end,
     to_xmerl(Elem#dbus_signal.args) ++ Result};

to_xmerl(#dbus_arg{}=Elem) ->
    {arg, 
     case list_to_binary(Elem#dbus_arg.name) of
	    undefined ->
	        [];
	    Name ->
	        [{name, Name}]
     end ++
     [{direction, Elem#dbus_arg.direction}, {type, Elem#dbus_arg.type}],  []}.

xml_event(startDocument, _L, S) ->
    S;

xml_event(endDocument, _L, #state{node=Node}) ->
    Node;

xml_event({startPrefixMapping, _Prefix, _Uri}, _L, S) ->
    S;

xml_event({endPrefixMapping, _Prefix}, _L, S) ->
    S;

xml_event({startElement, _, "node", _, Attrs}, _L, #state{s=root}=S) ->
    S#state{s=node, node=build_root_node(Attrs, #dbus_node{interfaces=gb_trees:empty()})};
<commit_msg>A number of values were assumed to be lists (strings), but in fact could be atoms; fixed their transformation to binaries.<commit_after>%%
%% @copyright 2006-2007 <NAME>
%% @Copyright 2014 <NAME>
%%
%% @author <NAME> <<EMAIL>>
%% @author <NAME> <<EMAIL>.
%% @doc introspect support module
%%
-module(dbus_introspect).

-include_lib("xmerl/include/xmerl.hrl").
-include("dbus.hrl").
-include("dbus_errors.hrl").

-export([
	 find_interface/2,
	 find_method/3,
	 find_signal/3,
	 to_xml/1,
	 from_xml/1,
	 from_xml_string/1
	]).

-record(state, {node         :: dbus_node(),
		child        :: dbus_node(),
		iface        :: dbus_iface(),
		method       :: dbus_method(),
		signal       :: dbus_signal(),
		property     :: dbus_property(),
		s     = root :: atom()}).

%%%
%%% API
%%%
-spec find_interface(dbus_node(), Iface :: dbus_name()) -> 
			 {ok, dbus_iface()} | {error, dbus_err()}.
find_interface(#dbus_node{interfaces=Ifaces}, IfaceName) ->
    case find_name(IfaceName, Ifaces, fun dbus_names:bin_to_iface/1) of
	{value, #dbus_iface{}=I} ->
	    {ok, I};
	none ->
	    {error, {'org.freedesktop.DBus.UnknownInterface', [IfaceName]}}
    end.


-spec find_method(dbus_node(), Iface :: dbus_name(), Method :: dbus_name()) -> 
			 {ok, dbus_method()} | {error, dbus_err()}.
find_method(#dbus_node{interfaces=Ifaces}=Node, IfaceName, MethodName) ->
    case find_name(IfaceName, Ifaces, fun dbus_names:bin_to_iface/1) of
	{value, #dbus_iface{methods=Methods}=_I} ->
	    case find_name(MethodName, Methods, fun dbus_names:bin_to_method/1) of
		{value, #dbus_method{}=Method} ->
		    {ok, Method};
		none ->
		    {error, {'org.freedesktop.DBus.UnknownMethod', {[MethodName], IfaceName, Node}}}
	    end;
	none ->
	    {error, {'org.freedesktop.DBus.UnknownInterface', [IfaceName]}}
    end.


-spec find_signal(dbus_node(), Iface :: dbus_name(), Signal :: dbus_name()) -> 
			 {ok, dbus_signal()} | {error, dbus_err()}.
find_signal(#dbus_node{interfaces=Ifaces}=Node, IfaceName, SignalName) ->
    case find_name(IfaceName, Ifaces, fun dbus_names:bin_to_iface/1) of
	{value, #dbus_iface{signals=Signals}} ->
	    case find_name(SignalName, Signals, fun dbus_names:bin_to_signal/1) of
		{value, #dbus_signal{}=Signal} ->
		    {ok, Signal};
		none ->
		    {error, {'org.freedesktop.DBus.UnknownSignal', {[SignalName], IfaceName, Node}}}
	    end;
	none ->
	    {error, {'org.freedesktop.DBus.UnknownInterface', [IfaceName]}}
    end.


to_xml(#dbus_node{}=Node) ->
    Prolog = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">",
    lists:flatten(xmerl:export_simple([to_xmerl(Node)], xmerl_xml, [{prolog, Prolog}])).

from_xml_string(Data) when is_binary(Data) ->
    Opts = [{event_fun, fun xml_event/3}, 
	    {event_state, #state{}},
	    skip_external_dtd],
    case xmerl_sax_parser:stream(Data, Opts) of
	{ok, #dbus_node{}=Node, _Rest} ->
	    Node;
	{_Tag, _Location, Reason, _EndTags, _State} ->
	    ?error("Error parsing introspection: ~p~n", [Reason]),
	    throw({error, parse_error})
    end.

from_xml(Filename) ->
    Opts = [{event_fun, fun xml_event/3}, 
	    {event_state, #state{}},
	    skip_external_dtd],
    case xmerl_sax_parser:file(Filename, Opts) of
	{ok, #dbus_node{}=Node, _Rest} ->
	    Node;
	{_Tag, _Location, Reason, _EndTags, _State} ->
	    ?error("Error parsing introspection: ~p~n", [Reason]),
	    throw({error, parse_error})
    end.

%%%
%%% Priv
%%%
find_name(Name, Tree, _) when is_atom(Name) ->
    case gb_trees:lookup(Name, Tree) of
	{value, Iface} -> {value, Iface};
	none -> gb_trees:lookup(atom_to_binary(Name, utf8), Tree)
    end;
find_name(Name, Tree, ToKnownFun) when is_binary(Name) ->
    case gb_trees:lookup(Name, Tree) of
	{value, Iface} -> {value, Iface};
	none -> 
	    case ToKnownFun(Name) of
		Bin when is_binary(Bin) -> none;
		Atom when is_atom(Atom) -> gb_trees:lookup(Atom, Tree)
	    end
    end.


to_binary(Item) when is_atom(Item) ->
    atom_to_binary(Item, utf8);
to_binary(Item) when is_list(Item) ->
    list_to_binary(Item).

to_xmerl(undefined) ->
    [];
to_xmerl(List) when is_list(List) ->
    lists:map(fun(Elem) -> to_xmerl(Elem) end, List);

to_xmerl(#dbus_node{}=Elem) ->
    {node,
     case to_binary(Elem#dbus_node.name) of
	 undefined ->
	     [];
	 Name ->
	     [{name, Name}]
     end,
     to_xmerl(Elem#dbus_node.elements) ++
     to_xmerl(Elem#dbus_node.interfaces)
    };

to_xmerl(#dbus_iface{}=Elem) ->
    {interface,
     [{name, to_binary(Elem#dbus_iface.name)}],
     to_xmerl(Elem#dbus_iface.methods) ++
     to_xmerl(Elem#dbus_iface.signals) ++
     to_xmerl(Elem#dbus_iface.properties)};

to_xmerl(#dbus_method{}=Elem) ->
    Result =
	case Elem#dbus_method.result of
	    none ->
		[];
	    undefined ->
		[];
	    Arg ->
		[to_xmerl(Arg)]
	end,
    {method,
     case to_binary(Elem#dbus_method.name) of
	    undefined ->
	        [];
	    Name ->
	        [{name, Name}]
     end,
     to_xmerl(Elem#dbus_method.args) ++ Result};

to_xmerl(#dbus_signal{}=Elem) ->
    Result =
	case Elem#dbus_signal.result of
	    none ->
		[];
	    undefined ->
		[];
	    Arg ->
		[to_xmerl(Arg)]
	end,
    {signal,
     case list_to_binary(Elem#dbus_signal.name) of
	    undefined ->
	        [];
	    Name ->
	        [{name, Name}]
     end,
     to_xmerl(Elem#dbus_signal.args) ++ Result};

to_xmerl(#dbus_arg{}=Elem) ->
    {arg, 
     case to_binary(Elem#dbus_arg.name) of
	    undefined ->
	        [];
	    Name ->
	        [{name, Name}]
     end ++
     [{direction, Elem#dbus_arg.direction}, {type, Elem#dbus_arg.type}],  []}.

xml_event(startDocument, _L, S) ->
    S;

xml_event(endDocument, _L, #state{node=Node}) ->
    Node;

xml_event({startPrefixMapping, _Prefix, _Uri}, _L, S) ->
    S;

xml_event({endPrefixMapping, _Prefix}, _L, S) ->
    S;

xml_event({startElement, _, "node", _, Attrs}, _L, #state{s=root}=S) ->
    S#state{s=node, node=build_root_node(Attrs, #dbus_node{interfaces=gb_trees:empty()})};

{'fraction_non_alphanumeric': 0.17615449202350966, 'fraction_numerical': 0.005625524769101596, 'mean_word_length': 3.5306200076074554, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 2, '>': 139, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 30}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2451768', 'n_tokens_mistral': 4893, 'n_tokens_neox': 4659, 'n_words': 809}